home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 6 / Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso / 025a / crdepp.zip / CRDE.DOC < prev    next >
Text File  |  1991-01-05  |  305KB  |  10,880 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.     The
  13.  
  14.     C
  15.  
  16.     Relational
  17.  
  18.     Database
  19.  
  20.     Engine
  21.  
  22.  
  23.             
  24.  
  25.  
  26.  
  27.  
  28.  
  29.  
  30.     Copyright 1990, Mark Lang.
  31.  
  32.  
  33.  
  34.  
  35.  
  36.  
  37.  
  38.  
  39.  
  40.  
  41.  
  42.  
  43.  
  44.  
  45.  
  46. Table of Contents
  47.  
  48.  
  49.  
  50. Chapter 1, Introduction    ...............................................    1
  51.  
  52. Chapter 2, Getting Started ............................................    3
  53.        System Requirements ............................................    3
  54.        Program capacities .............................................    3
  55.        Installing CRDE ................................................    3
  56.        What comes with CRDE ...........................................    4
  57.        Using CRDE with Turbo C ........................................    5
  58.          Linking  CRDE ............................................    5
  59.          Reserved function calls ..................................    5
  60.          Using CRDE in your program    ...............................    5
  61.          Using disk caches with CRDE ..............................    6
  62.        CRDE tables ....................................................    6
  63.        Basic terminology ..............................................    7
  64.  
  65. Chapter 3, Creating Tables ............................................    8
  66.        The TABLE type .................................................    8
  67.        Table descriptors ..............................................    9
  68.        Defining a primary key .........................................    10
  69.        Creating a database ............................................    12
  70.           The kitchen sink approach    ...............................    12
  71.           The relational approach .................................    13
  72.  
  73. Chapter 4, Filling Tables with Information ............................    14
  74.        Loading  rows with tload    .......................................    14
  75.        Error -41 ......................................................    15
  76.        Inserting rows .................................................    16
  77.        Replacing rows .................................................    17
  78.        Write modes ....................................................    17
  79.  
  80. Chapter 5, Basic Table I/O ............................................    19
  81.        Opening tables .................................................    19
  82.        Integrity checking .............................................    19
  83.        Closing  a table    ...............................................    19
  84.        Checking table accessability ...................................    20
  85.        Renaming tables ................................................    20
  86.        Erasing tables .................................................    20
  87.        Secondarr indexes ..............................................    20
  88.           Creating indexes ........................................    20
  89.           Index descriptors    .......................................    21
  90.           Primary index vs Secondary indexes ......................    22
  91.           Rebuilding indexes ......................................    23
  92.           Dropping indexes ........................................    23
  93.           Redundant indexes    .......................................    23
  94.  
  95. Chapter 6, Viewing a Table ............................................    24
  96.  
  97.  
  98.  
  99.  
  100.  
  101. Chapter 7, The Search-Related Functions    ...............................    26
  102.        The seven search related functions .............................    26
  103.        The search expression ..........................................    26
  104.        The search engine ..............................................    27
  105.        Search related operations ......................................    27
  106.           Selecting rows ..........................................    27
  107.           Projecting rows .........................................    28
  108.           Deleting rows ........................................... 29
  109.           Changing rows ........................................... 30
  110.           Scanning rows ...........................................    30
  111.           Looking up a row ........................................    31
  112.           Getting rows into C variables ...........................    31
  113.        "..if" functions    ...............................................    32
  114.  
  115. Chapter 8, Miscelleneous operations ...................................    36
  116.        Restructuring tables ...........................................    36
  117.        Renaming columns    ............................................... 37
  118.        Getting info from your tables ..................................    38
  119.        Importing and Exporting data ................................... 39
  120.           Ascii files .............................................    39
  121.           dBASE files .............................................    41
  122.  
  123. Chapter 9, Working with Dates ......................................... 43
  124.        The date_t type and date math ..................................    43
  125.        Converting calender dates ......................................    43
  126.        Miscelleneous date functions ...................................    44
  127.        Month and year math ............................................    45
  128.        Day of week functions ..........................................    45
  129.  
  130. Chapter 10,  Temporary Tables .........................................    47
  131.        STATIC tables ..................................................    47
  132.        Virtual tables .................................................    47
  133.        TEMP tables and auto-cleanup ...................................    48
  134.        Rules for using TEMP tables ....................................    49
  135.  
  136. Chapter 11, Performing Queries ........................................    50
  137.        What is a query?    ...............................................    50
  138.        Simple Queries    ...............................................    52
  139.           Simple select queries ................................... 52
  140.           ORing queries ........................................... 53
  141.           Projecting columns ......................................    54
  142.           Selecting distinct values    ............................... 55
  143.           Joining tables ..........................................    55
  144.           Order By queries ........................................ 57
  145.           Statistical queries, making calculations ................    58
  146.           Set-related queries .....................................    59
  147.        Complex Queries ................................................ 60
  148.           Subqueries ..............................................    60
  149.           Correlated subqueries ...................................    60
  150.           Multi-table joins    ....................................... 61
  151.           "In" subqueries ......................................... 62
  152.           Multiple subqueries ..................................... 62
  153.           Group By queries ........................................ 63
  154.              Writing grouping functions    .......................    64
  155.              Group Having queries ............................. 68
  156.           Set Group queries    ....................................... 69
  157.        Optimizing Queries ............................................. 73
  158.  
  159.  
  160.  
  161. Chapter 12, Creating Reports .......................................... 74
  162.        Single table reports ........................................... 74
  163.        Multi-table reports ............................................    75
  164.  
  165. Chapter 13, Maintaining Database Integrity ............................    79
  166.        Entity integrity    ...............................................    79
  167.        Domain integrity    ...............................................    79
  168.        Referential integrity .......................................... 80
  169.           Foreign an primary keys ................................. 80
  170.           and inserting rows ...................................... 80
  171.           and deleting rows    ....................................... 81
  172.           and changing rows    ....................................... 82
  173.  
  174. Chapter 14, Performing Tansactions ....................................    84
  175.        CRDE's transaction tracking features ...........................    84
  176.        How does transaction tracking work? ............................    85
  177.  
  178. Chapter 15, Reparing Tables ...........................................    86
  179.        Corrupted indexes ..............................................    86
  180.        Testing your indexes ...........................................    86
  181.        Repairing a table with trepair .................................    86
  182.        Limitations of trepair .........................................    87
  183.  
  184. Reference Guide    .......................................................    88
  185.        Standard Conventions ...........................................    88
  186.           Return values ...........................................    88
  187.           Functions which return tables ...........................    89
  188.           Parameters in CRDE functions ............................ 90
  189.           Auto-cleanup of TEMP tables .............................    90
  190.        Function library    ...............................................    92
  191.  
  192. Global Variables ......................................................    170
  193.  
  194. Appendix A, Error Codes    ...............................................    173
  195.  
  196.  
  197.  
  198.  
  199.  
  200.  
  201.  
  202.  
  203.  
  204.  
  205. Chapter 1
  206.  
  207. Introduction
  208.  
  209.  
  210.  
  211. The C Relational Database Engine (called CRDE hereafter) is a library
  212. of over 90 functions which support the creation, maintenance, and
  213. querying of single-user databases.  CRDE is not another B-tree library.
  214. It goes far beyond the capabilities of most data management libraries.
  215. Complex operations such as sorting, restructuring, and joining tables
  216. are single line function calls in CRDE.  In terms of ease of use,
  217. relational querying power, and sheer speed, CRDE is on par with
  218. professional relational database managent systems.
  219.  
  220. Below are listed just some of the features of CRDE:
  221.  
  222.     CRDE uses a sophisticated file and memory manager to
  223.     manage  tables and indexes. The manager intelligently
  224.     coordinates all  your resources for maximum performance. It
  225.     also features support  for transaction tracking, without a
  226.     network.
  227.  
  228.     CRDE tables each have their own data dictionary which
  229.     contains  information about the columns and indexes for a
  230.     table. Among other things, CRDE uses  this information to
  231.     maintain and use indexes transparently.
  232.  
  233.     CRDE can match the relational querying power of high level
  234.     query  languages such as SQL, often in as little as one or two
  235.     function calls.  However CRDE is not an SQL engine and
  236.     does not require a  preprocessor.
  237.  
  238.     CRDE requires no special initialization calls or setup, and is
  239.     not  a TSR.  Simply link the library and use the functions.
  240.  
  241.     CRDE uses C data types and stores data as C structures,
  242.     making  sharing data between C and CRDE tables very easy.
  243.  
  244.     CRDE has a complete date library for managing dates and
  245.     performing date math.
  246.  
  247.     CRDE introduces the TABLE datatype which is the heart of
  248.     CRDE.  A TABLE is very similar to a FILE in terms of use
  249.     and design,  although much more powerful. Functions can
  250.     accept tables as  parameters and return tables as results.
  251.  
  252.     CRDE comes with a view facility which you can integrate
  253.     directly into  your programs. With it you can view any table
  254.     in any sorted order,  selecting only those columns you wish to
  255.     display, in any window you want to view the table in, with a
  256.     single function call.
  257.  
  258.  
  259.                      - 1 -
  260.  
  261.  
  262. This manual assumes that you are a fairly experienced C programmer with
  263. an understanding of the relational database model. Familiarity with a
  264. high level relational database language and/or SQL is also recommended.
  265. You should also be familiar with Turbo C 2.0 and how to link and use
  266. library modules with your own programs.
  267.  
  268.  
  269.  
  270.  
  271.  
  272.  
  273.  
  274.  
  275.  
  276.  
  277.  
  278.  
  279.  
  280.  
  281.  
  282.  
  283.  
  284.  
  285.  
  286.  
  287.  
  288.  
  289.  
  290.  
  291.  
  292.  
  293.  
  294.  
  295.  
  296.  
  297.  
  298.  
  299.  
  300.  
  301.  
  302.  
  303.  
  304.  
  305.  
  306.  
  307.  
  308.  
  309.  
  310.  
  311.  
  312.  
  313.  
  314.  
  315.  
  316.  
  317.  
  318.  
  319.                      - 2 -
  320.  
  321.  
  322.  
  323.  
  324.  
  325. Chapter 2
  326.  
  327. Getting Started
  328.  
  329.  
  330.  
  331. System Requirements
  332.  
  333. To use CRDE you must have all of the following:  
  334.  
  335.  - An IBM PC, XT, AT, PS/2 or 100% compatible computer.
  336.  
  337.  - At least 512k of internal memory.
  338.  
  339.  - A hard drive.
  340.  
  341.  - DOS 2.0 or greater.
  342.  
  343.  - Turbo C 2.0.
  344.  
  345. You should also make sure that your CONFIG.SYS file contains the statement
  346.  
  347.     FILES = 20
  348.  
  349. in it.  Although this setting is not mandetory, it is the recommended
  350. setting for using CRDE for optimal performance.
  351.  
  352.  
  353. Program Capacities
  354.  
  355. In CRDE, a single table may have up to 
  356.  
  357.  - 2 billion rows with up to 4000 bytes each.
  358.  
  359.  - 256 columns with up to 256 characters each.
  360.  
  361.  - 1 primary index composed of up to 8 columns.
  362.  
  363.  - 8 secondary indexes composed of up to 8 columns each.
  364.  
  365.  
  366. Installing CRDE
  367.  
  368. Before you can use CRDE it must be installed on your computer. CRDE is
  369. not copy-protected, so installation is a fairly straight-forward.
  370. Simply copy the contents of the CRDE diskette into your working
  371. directory.  For example, if your working directory is C:\TURBOC, place
  372. the CRDE diskette into drive A: and type
  373.  
  374.     C:>copy A:*.* C:\TURBOC 
  375.  
  376.  
  377.  
  378.  
  379.                      - 3 -
  380.  
  381. to copy CRDE into your hard drive.  Specifically, Turbo C must be able to
  382. access 3 files on your CRDE diskette, crde.lib, crde.h, and date.h.
  383.  
  384.  
  385. What comes with CRDE
  386.  
  387. There are 18 files on the CRDE diskette.  Only the first three are
  388. required to create and compile applications with CRDE. The rest are
  389. sample programs which you may run and examine at your convenience.
  390.  
  391. crde.lib    This is the CRDE library. You must link this to any
  392.         application which uses CRDE.
  393.  
  394. crde.h       This header file contains prototypes of the bulk of CRDE
  395.         functions plus several pre-defined macros used by CRDE.
  396.         Include this in any source file which uses CRDE functions.
  397.  
  398. date.h      This header file contains prototypes for all the date
  399.         functions in the CRDE library. It also defines the
  400.         date_t type used by CRDE. Include this in any source
  401.         file which uses any of CRDE's date functions or the
  402.         date_t datatype.
  403.  
  404. read.me            Ascii file containing last minute information about
  405.         bug-fixes or updates not found in the CRDE manual which
  406.         can be loaded into virtually any editor for inspection.
  407.         Your CRDE diskette may or may not contain this file.
  408.  
  409. sample.exe     The sample application provided with CRDE which
  410.         illustrates some of CRDE's features.  sample.exe
  411.         maintains a small, fully-functional, library database.
  412.  
  413. sample.c    Source code to the sample application.
  414.  
  415. sample.prj       Project file for creating the sample application.  
  416.  
  417. tview.c        Program written using CRDE functions to view any CRDE
  418.         table from the command line.
  419.  
  420. tstruct.c     Program written using CRDE functions to view any CRDE
  421.         table's structure from the command line.
  422.  
  423. texport.c     Program written using CRDE functions to export any CRDE
  424.         table to ascii or dBASE format.
  425.  
  426.  
  427.  
  428.  
  429.  
  430.  
  431.  
  432.  
  433.  
  434.  
  435.  
  436.  
  437.  
  438.  
  439.                      - 4 -
  440.  
  441. Using CRDE with Turbo C
  442.  
  443. Linking  CRDE
  444.  
  445. To use CRDE include the crde.h header file in any source file containing
  446. CRDE function calls. You may also need to include the date.h file if
  447. you use any of CRDE's date functions. Programs written using CRDE
  448. functions must be compiled in the LARGE memory model and linked to the
  449. CRDE library crde.lib.  To do this from the integrated environment you
  450. must create a project file to let Turbo C know that you want it to link
  451. the CRDE library with your program.  An example project file to create
  452. an executable file for a program "noname.c" might look like the following:
  453.  
  454.     noname
  455.     crde.lib
  456.  
  457. See your Turbo  C user's manual for more information on creating project
  458. files.  You can also link you program from the command line using TLINK.
  459. To create an executable file using TLINK for the same program "noname.c"
  460. would look something like this:
  461.  
  462.   tlink  noname.obj c0l.obj,,, emu.lib mathl.lib cl.lib crde.lib
  463.  
  464. It is also a good idea when compiling programs using CRDE to set the
  465. "Options\Linker\Warn duplicate symbols" On. This will keep you from
  466. accidentally duplicating a CRDE function within your own program and
  467. causing a program crash.
  468.  
  469.  
  470. Reserved function calls
  471.  
  472. The CRDE library is made up of a central engine TENGINE, plus object
  473. code for each CRDE function.  TENGINE consists of 100 "engine calls"
  474. which are used reserved specifically for use by CRDE functions.  The
  475. CRDE engine calls have the format _CRDE_XX where XX is a number from
  476. 00 to 99.  Calling any of these routines directly will most likely
  477. cause your program to hang.
  478.  
  479.  
  480. Using CRDE in your program
  481.  
  482. CRDE requires no special initialization or setup.  Simply link the
  483. library and use the functions.  However, every program which uses CRDE
  484. must contain and assign the global variable tbuffers.   This variable
  485. tells CRDE how much memory to set aside for its exclusive use.  CRDE
  486. allocates memory in 512 byte blocks or buffers.  For example, the
  487. following would set aside 64k of memory for use by CRDE's functions:
  488.  
  489.     int tbuffers = 128;
  490.  
  491.     int main()
  492.     {
  493.         /* program here */
  494.     }
  495.  
  496.  
  497.  
  498.  
  499.                      - 5 -
  500.  
  501. tbuffers must be declared globally.  CRDE requires the tbuffers
  502. variable to run.  Fortunately, the linker will tell you if you forget
  503. to declare tbuffers.
  504.  
  505. Assigning a good value to tbuffers depends upon many factors:  how much
  506. memory is avaiable, the number and sizes of your tables, how much memory
  507. your program needs, if you do large transactions on tables, etc.  A
  508. good minimal value for most programs is 128 buffers.  This value
  509. provides solid performance for most operations and only uses 64k of
  510. memory.   For optimal performance in programs which are database
  511. intensive or make use of CRDE's transaction tracking facilities should
  512. consider a setting of 256 or 512 buffers or more if memory allows.
  513. CRDE requires a minium of at least 64 buffers (32k) to run efectively.
  514.  
  515. Note:  CRDE allocates its buffers according to tbuffers automatically
  516. after the first table is opened or created.  Changing the value of
  517. tbuffers after that will not effect the amount of buffers used by CRDE.
  518.  
  519.  
  520. Using disk caches with CRDE
  521.  
  522. Do not use disk caches, heap expanders, or similar types of programs in
  523. conjunction with CRDE.  CRDE has a sophisticated built-in file and memory
  524. manager which has all the capabilities of the above programs and more.
  525. CRDE knows about which tables and indexes will involved in an operation
  526. while these other program do not.  Using them will interfere with CRDE
  527. and will probably impede performance rather than improve it.
  528.  
  529.  
  530. CRDE tables
  531.  
  532. A CRDE table consists of one or more files.  Specifically, a table can
  533. consist of the table itself, a primary index, and up to 8 secondary
  534. indexes.  Although each of these objects consists of a separate file on
  535. disk, CRDE considers them to be a unit.
  536.  
  537. On disk, all the objects related to a single table have the same base
  538. name as the table and are distinguished from each other by their
  539. filename extension.
  540.  
  541.     Extension     Type of Object
  542.  
  543.     .t        table
  544.     .p         primary index
  545.      .sX        secondary index
  546.  
  547. The X in a secondary indexes extension stands for a digit from 0 to 7.
  548. A table's base name may be any legal DOS filename, but may not include
  549. wildcards or an extension.
  550.  
  551.  
  552.  
  553.  
  554.  
  555.  
  556.  
  557.  
  558.  
  559.                      - 6 -
  560.  
  561. Basic Terminology
  562.  
  563. The following is a set of basic terms you should be familiar with before
  564. using CRDE.
  565.  
  566. database    A database is a collection of information or data
  567.         pertaining to a specific project.  For instance, a
  568.         library database would contain all the information
  569.         about books, members, loans, etc. in the library.  A
  570.         relational database consists of one or more tables.
  571.  
  572. table        A table is a discrete set of information within a
  573.         database.  A table can be visualized as a two
  574.         dimensional chart with rows and columns.  In  C, a
  575.         table might be more analogous to an array of
  576.         structures.  CRDE also considers any indexes created
  577.         on the table to be part of the table.
  578.  
  579. row        A row is a horizontal slice of a table. In C, a row is
  580.         similar to an item in an array.  The terms row and
  581.         record are interchangeable in CRDE.
  582.  
  583. column         A column is a vertical slice of a table.  In C, a
  584.         column is similar to a single field in a structure.
  585.         The terms column and field are interchangeable in CRDE.
  586.  
  587. index        An index is a special file organized for quick access
  588.         to information in a table.  An index on a table is
  589.         similar to an index in a book.  CRDE can use an index
  590.         to lookup information very quickly.
  591.  
  592. key        A key describes the columns which make up an index.  In
  593.         CRDE, up to 8 columns can be part of a key.
  594.  
  595.  
  596.  
  597.  
  598.  
  599.  
  600.  
  601.  
  602.  
  603.  
  604.  
  605.  
  606.  
  607.  
  608.  
  609.  
  610.  
  611.  
  612.  
  613.  
  614.  
  615.  
  616.  
  617.  
  618.  
  619.                      - 7 -
  620.  
  621.  
  622.  
  623.  
  624.  
  625.  
  626. Chapter 3
  627.  
  628. Creating Tables
  629.  
  630.  
  631.  
  632. The TABLE type
  633.  
  634. CRDE introduces a new data type, TABLE, which is much akin to the FILE
  635. type defined in the C standard library, although much more powerful,
  636. as you shall see.  Tables can be assigned to variables, passed as
  637. parameters, and returned by functions.  This object-oriented approach
  638. to data management, including the casual way in which you can create
  639. and manipulate tables, is at the heart of CRDE's power.  You can
  640. declare variable as TABLE * just as you would a FILE *. For example,
  641. to declare variable t as a TABLE would look like the following: 
  642.  
  643.  
  644.     TABLE *t;
  645.  
  646. Note:  you cannot declare a variable to be of type TABLE.  It must be
  647. a TABLE *.  Doing so will cause a compilation error.
  648.  
  649. You can create a new table with the tcreat function.  The following
  650. program creates a simple table called "sups" containing 4 columns: s#,
  651. sname, city, status, and assigns it to t.
  652.  
  653.     #include <stdio.h>  
  654.     #include "crde.h"  
  655.  
  656.     int main()  
  657.     {   
  658.       TABLE *t;
  659.   
  660.       t = tcreat("customer", "s# i, sname c26, city c26, status i");
  661.       tclose(t);
  662.     }
  663.  
  664. tcreat is declared in crde.h as:  
  665.  
  666.     TABLE *tcreat(char *name, char *td);
  667.  
  668. The first parameter name is the name of the table.  name must be a legal
  669. DOS pathname <= 64 characters. It may not contain wildcards or an
  670. extension.
  671.  
  672.  
  673.  
  674.  
  675.  
  676.  
  677.  
  678.  
  679.                      - 8 -
  680.  
  681. Table descriptors
  682.  
  683. The second parameter td is known as the table descriptor.  The table
  684. descriptor defines the columns in the table, their names, types and
  685. sizes.  A table descriptor should be an asciiz string of the format:
  686.  
  687.     "colname1 coltype1 colname2 coltype2 colname3 coltype3 ..."
  688.  
  689. Column names can consist of any characters except whitespace.  White-
  690. space characters are all characters classified by the standard library
  691. function isspace() plus the comma ','.  Thus column names can contain
  692. letters, numbers, and other characters like '#' and '-'.  However, you
  693. should avoid using the '*' and '!' characters in column names since
  694. they have special meaning in CRDE.  A table may contain up to 256
  695. columns.
  696.  
  697. A column name should describe the contents of the column.  A table may
  698. not contain two columns with the same name.  Column names are case-
  699. sensitive however, so "Name", "NAME", and "name" are all considered
  700. different.  Every column name in a table descriptor must be followed
  701. by a datatype which defines the type of the column. Column names and
  702. types must be individual tokens, i.e. separated by whitespace.
  703.  
  704. CRDE supports 6 datatypes:  c, i, l, d, f, and $.  Each type directly
  705. relates to a native C datatype.
  706.  
  707. c       A character string between 1 and 256 characters. The c datatype
  708.     must be immediately followed by a number between 1 and 256.
  709.     This is the maximum length of the string. The c type is
  710.     equivalent to the C type char[].  Thus the type c26 is equivalent
  711.     to char[26], and so on.
  712.  
  713. i       An integer value corresponding to the C type int.  In Turbo C,
  714.     an int has a range from -32767 to 32767.
  715.  
  716. l       A long integer value corresponding to the C type long int.  In
  717.     Turbo C, a long int has a range from -2147483647 to 2147483547.
  718.  
  719. d       A date.  The d type corresponds to the date_t type defined in
  720.     date.h.  The Turbo C version of date_t is equivalent to type
  721.     long int.  CRDE supplies several functions for creating and
  722.     manipulating dates.  A date type can hold any date between
  723.     1/1/1 and 12/31/32767.
  724.  
  725. f       A floating point value.  The f type directly corresponds to the
  726.     C type double.  In Turbo C, a double has a range of 1.7E-308 to
  727.     1.7E+308.
  728.  
  729.  
  730. $       A dollar value. The $ type corresponds to the dollar_t type
  731.     defined in crde.h, which in the Turbo C version is type double.
  732.  
  733.  
  734.  
  735.  
  736.  
  737.  
  738.  
  739.                      - 9 -
  740.  
  741. Since all CRDE datatypes directly correspond to C datatypes, it makes
  742. sense that a row in CRDE then, would correspond to a C structure.  This
  743. is an important feature of CRDE, making transferring data between CRDE
  744. tables and C variables relatively easy.  A row in the sups table would
  745. correspond to the C declaration
  746.  
  747.     struct {  
  748.         int s;  
  749.         char sname[26];  
  750.         char city[26];  
  751.         int status;
  752.     }
  753.  
  754. This becomes important when you want to transfer data between C
  755. variables and CRDE tables.  Note: since C variable names cannot contain
  756. characters like '#', they were omitted from the declaration. In fact,
  757. none of the column names have to appear in the structure, only their
  758. types and sizes are important.
  759.  
  760.  
  761. Defining a primary key
  762.  
  763. CRDE supports the relational idea of defining a primary key on a table.
  764. The primary key is made of one or more column(s) of the table.  A table
  765. which has a primary key is generally referred to as being a keyed table.
  766. Similarly, a table without a primary key is referred as being non-keyed.
  767. The key values of a row in the table consist of all the values in every
  768. column in the row which make up the primary key.  By definition, every
  769. row in a keyed table can be uniquely identified by its key value(s).
  770. Consequently, no two rows in a table can have the same key value(s).
  771.  
  772. A primary key prevents you from adding rows to a table which have the
  773. same key values.  In CRDE, up to 8 columns may be part of the primary
  774. key. CRDE prevents two or more rows in a table from having identical
  775. values in each of the assigned columns.  CRDE also supports non-keyed
  776. tables (tables which do not have a primary key).  It is good relational
  777. practice, however, to define a primary key on a table.  Doing so will
  778. help you to normalize your tables and have a minimum amount of redundant
  779. information, and can also improve search times.  Tables are usually
  780. linked to one another by all or part of their primary keys. 
  781.  
  782. You can define a primary key in a table descriptor by prepending any
  783. columns you want as part of the key with an asterisk '*'.  The '*' must
  784. be directly attached to the column's name, and not be separated by
  785. whitespace.  Columns which are part of the primary key can be in any
  786. order, and do not have to be consecutive.  However, the order can have
  787. other side effects. In general, you should place all key columns at the
  788. beginning of a table, with the most significant column first, the next
  789. most significant column, second, etc.  In the sups table above, the "s#"
  790. column is the key field of the table. It uniquely identifies a row, thus
  791. every row should have a unique value for "s#".  To make "s#" the primary
  792. key of the table you would define the table as
  793.  
  794.     t = tcreat("sups", "*s# i, sname c26, city c26, status i");
  795.  
  796. This would prevent you from entering to suppliers who have the same "s#".  
  797.  
  798.  
  799.                      - 10 -
  800.  
  801. As previously stated, a primary key can be made of up to 8 columns.
  802. This orders table contains a supplier # "s#", a part# "p#" and a
  803. quantity "qty".  The primary key is on both "s#" and "p#".
  804.  
  805.     t = tcreat("orders", "*s# i, *p# i, qty i");
  806.  
  807. This table would allow you to have orders with the same supplier # or
  808. part # but not both.
  809.  
  810. Defining a primary key in CRDE has another advantage as well.  To
  811. enforce the primary key, CRDE creates an index, called the primary
  812. index, on the table.  Whenever you insert a row into a table, CRDE
  813. uses this index to make sure that the primary key will not be
  814. violated. However, CRDE can also use this index to help speed up
  815. searches and other relational operations.  Since the bulk of relational
  816. operations will involve the primary key of a table, this will
  817. significantly improve the performance of many operations.  You never
  818. have to remind CRDE to update the primary index. CRDE updates it
  819. automatically whenever you insert, change, or delete rows in a table,
  820. using any CRDE function, so it is always kept up to date.
  821.  
  822. When designing a table, your first thought should be of what will be
  823. the table's primary key.  The key should be a value which would not be
  824. duplicated under normal circumstances, such as an id# or some other
  825. unique identification value.  A poor choice for a primary key, usually
  826. made by beginners, is a name or some other large character field.
  827. But who is to say that two suppliers might not have the same name?
  828. Furthermore, you might accidently misspell the name, or use the wrong
  829. case, leave trailing blanks on the end of it, making it difficult to
  830. correctly identify duplicate keys.  
  831.  
  832. The best choices for key columns are columns which have the types i,
  833. l, and d.  Short c columns can also make good keys if used correctly
  834. (for example, an id which contains letters in it, such as a serial#).
  835. c columns longer 15 or 20 characters are probably poor choices for keys,
  836. although CRDE support keys up to 256 characters in length.  Not all
  837. columns in the primary key do not have to be of the same type either.
  838. You may frequently have primary keys which are made of i, l, d, and c
  839. columns in combination.
  840.  
  841. f and $ columns are the worst choices for primary keys, although CRDE
  842. does support them as such. This is because of the nature of floating
  843. point values on computers. A computer can only measure a floating point
  844. value to a certain number of significant digits, whereas it can
  845. calculate i, l, c, and d columns exactly. Thus, the results of floating
  846. point expressions are not always accurate.  Rather they are
  847. approximations of what the result should be.  Consider the problem of
  848. a simple floating point division:
  849.  
  850.     double f = 1.0 / 3.0; 
  851.  
  852. The answer to this problem is f = 0.333333333 etc...  But since the
  853. computer can only store 19-20 digits, it must truncate the value.  The
  854. result is not really 1.0 / 3.0, but an approximation.  Two approximations
  855. of the same value might not always be identical.  The bottom line is
  856. that you should avoid using f or $ columns in primary keys if at all
  857. possible.  
  858.  
  859.                      - 11 -
  860.  
  861. Creating a database
  862.  
  863. Creating a good relational database can be challenging, especially if
  864. you are not familiar with the relational model.  Beginners will often
  865. try to lump all data together into a single table rather than distribute
  866. data among several tables and relate them together.  This can lead to
  867. databases which are very redundant and inflexable.
  868.  
  869.  
  870. The kitchen sink approach
  871.  
  872. Suppose you wanted to maintain a list of orders made for parts from
  873. various suppliers.  Your first draft of the database looks like this
  874.  
  875.     orders = tcreat("orders", "sname c26, city c26, status i,"
  876.       "pname c26, color c11, qty i");
  877.  
  878. Each order contains the supplier's name, city, and  status, the part's
  879. name and color, and the quantity ordered.  This seems harmless enough.
  880. Now lets make some orders:
  881.  
  882. sname        city        status     pname        color    qty
  883. ---------------    ---------------    -------    ---------------    -------    -------
  884. Smith, John    Boston        5    bolt        red    100
  885. Smith, John    Boston        5    screw        red    200
  886. Smith, John    Boston        5    screw        blue    300
  887. Smith, John    Boston        5    nut        gray    400
  888. Smith, John    Boston        5    bolt        gray    500
  889. Smith, John    Boston        5    nail        red    600
  890. Jones, Ben    New York    10    bolt        red    100
  891. Jones, Ben    New York    10    screw        red    100
  892. Jones, Ben    New York    10    nail        red    100
  893. Barnes, Frank    Boston        15    bolt        red    200
  894. Barnes, Frank    Boston        15    screw        red    50    
  895. Landon, Mark    Auburn        20    screw        blue    100
  896. Landon, Mark    Auburn        20    bolt        gray    500
  897.  
  898.  
  899. As you add more and more information to the table, the problems with
  900. this arrangement become apparent.  There is a large amount of redundant
  901. information in the table.   The same names and cities appear several
  902. times in the table.  Every time an order is entered, redundant
  903. information is added over and over again.
  904.  
  905. This type of organization is also very inflexible.  For example, if
  906. John Smith moved to Chicago, you would have to go through and change
  907. every row which contained John Smith.  Suppose another supplier named
  908. John Smith lived in Boston.  Although unlikely, it is not impossible.
  909. There would be no way to differentiate between the two.
  910.  
  911.  
  912.  
  913.  
  914.  
  915.  
  916.  
  917.  
  918.  
  919.                      - 12 -
  920.  
  921. The relational approach
  922.  
  923. The relational solution to this problem is to divide the one large
  924. table into smaller tables by organizing data into groups:  suppliers,
  925. parts, and orders.  Suppliers and parts are given special id #'s so
  926. that they may be easily identified.  The orders table references the
  927. supplier and part tables though its "s#" and "p#" columns.
  928.  
  929.     sups = tcreat("sups", "*s# i, sname c26, city c26, status i");
  930.     parts = tcreat("parts", "*p# i, pname c16, color c11");
  931.     orders = tcreat("orders", "*s# i, *p# i, qty i");
  932.  
  933. sups
  934.  
  935.     s#     sname        city        status
  936.     -------    --------------- ---------------    -------
  937.     1    Smith, John     Boston        5
  938.     2    Jones, Ben      New York    10
  939.     3    Barnes, Frank    Boston        15
  940.     4    Landon, Mark    Auburn        20
  941.     5    Andrews, Jim    New York    5
  942.  
  943.  
  944. parts
  945.  
  946.     p#    pname        color
  947.     -------    ---------------    -----------
  948.     1    bolt        red
  949.     2    screw        red
  950.     3    screw        blue
  951.     4    nut        gray
  952.     5    bolt        gray
  953.     6    nail        red
  954.  
  955. orders
  956.  
  957.     s#    p#    qty
  958.     -------    -------    -------
  959.     1    1    100
  960.     1    2    200
  961.     1    3    300
  962.     1    4    400
  963.     1    5    500
  964.     1    6    600
  965.     2    1    100
  966.     2    2    100
  967.     2    6    100
  968.     3    1    200
  969.     3    2    50
  970.     4    3    100
  971.     4    5    500
  972.  
  973.  
  974. Breaking up your tables in this manner is called normalization.  A table
  975. is said to be normalized if it contains a minimum amount of redundant
  976. information.
  977.  
  978.  
  979.                      - 13 -
  980.  
  981.  
  982.  
  983.  
  984.  
  985.  
  986. Chapter 4
  987.  
  988. Filling Tables with 
  989. Information
  990.  
  991.  
  992.  
  993. Loading tables with tload
  994.  
  995. Now that we have a sample database set up, we need to fill it with
  996. information.  CRDE provides 3 functions for inputing data into tables:
  997. tload, tinsert, and treplace.  The simplest of the three is tload.
  998. tload is declared in crde.h as  
  999.  
  1000.     int tload(TABLE *t, ...);
  1001.  
  1002. tload expects a variable series of arguments, one for every column in t. 
  1003. Together the arguments form a row.  tload then inserts the row into the
  1004. table.  For example, to load a supplier s# = 1, sname = "Smith, John",
  1005. city = "Boston", status = 5, into the sups table would look like
  1006.  
  1007.     tload(sups, 1, "Smith, John", "Boston", 5);
  1008.  
  1009. The table sups now contains one row.  Data must be entered for every
  1010. column in the exact same order that they appear in the table's table
  1011. descriptor, and you may not skip column(s).  The argument for each
  1012. column must be the exact type for that column (c columns expect a
  1013. char[], or char *, as an argument).  If an argument is not of the
  1014. correct type, the data you are loading may not be what you expect.
  1015.  
  1016. This problem can commonly occur in l columns.  For example, suppose
  1017. the sups table were defined as
  1018.  
  1019.     sups = tcreat("sups", "*s# l, sname c26, city c26, status i");
  1020.  
  1021. the above tload statement would be invalid.  Why?  because the first
  1022. argument is an int value whereas the "s#" column is of type l or long
  1023. int.  By default, C converts constant 1 into an  int.  To load 1 as the
  1024. suppler number you would have to explicitly cast it as type long by
  1025. using a typecast (long int) or the L suffix.  A similar problem would
  1026. occur is "s#" were of type f  (which in this case it  shouldn't be
  1027. since it is part of the primary key).  A way around having to use a
  1028. specific typecast with f columns is to let the compiler know that you
  1029. are entering a floating-point value by writing the number as 1.0.
  1030. C interprets floating point constants to be of type double by default.
  1031. In a similar vein, you should be sure to make sure that the constant
  1032. value is not out of range.  For example, loading 1000000 into an int
  1033. column (in Turbo C anyway).  tload does handle the problem of char *
  1034.  
  1035.  
  1036.  
  1037.  
  1038.  
  1039.                      - 14 -
  1040.  
  1041. arguments which are too long however. If the argument for a c column
  1042. to too large to fit in the column, tload will automatically truncate
  1043. it to fit into the column's specified length.  tload does no type
  1044. checking, nor does it verify that the correct number of arguments have
  1045. been entered.  Entering the wrong number of arguments or the wrong type
  1046. for an argument will leave part or all of the columns in the row
  1047. undefined.
  1048.  
  1049. You can load any datatype into tables, even date types.  For example,  
  1050.  
  1051.     emps = tcreat("emps", "e# i, ename c26, hired d, salary f");
  1052.     tload(emps, 100, "Barnes, Don", mkdate(4, 2, 1978), 35000.0);
  1053.     tload(emps, 101, "Carnegie, Jamie", mkdate(5, 7, 1988), 25000.0);
  1054.     tload(emps, 102, "Donald, Markus", mkdate(11, 13, 1985), 42500.0);
  1055.  
  1056. mkdate is declared in date.h.  It receives three parameters, month,
  1057. day, and year, and returns the date_t equivalent.  Also note how the
  1058. salaries and appended with a .0 to mark them as double values.  You
  1059. can also use variables in tload.  Using variables can be safer than
  1060. constants because C converts a constant to the correct type when it
  1061. assigns a variable.
  1062.  
  1063. The following function demonstrates this point by loading a supplier
  1064. into the sups table based upon the parameters received.  Any constants
  1065. passed to LoadSup are automatically converted to the correct type by C
  1066. for you.  Using this method also eliminates any problems with entering
  1067. the correct number of arguments.  C (new-style anyway) will prevent you
  1068. from entering the wrong number and/or types into the row by issuing a
  1069. compilation error.  
  1070.  
  1071.     TABLE *sups;
  1072.  
  1073.     int LoadSup(long s, char *sname, char *city, int status)  
  1074.     {    
  1075.         return tload(sups, s, sname, city, status);  
  1076.     }  
  1077.  
  1078.     sups = tcreat("sups", "*s# l, sname c26, city c26, status i");
  1079.     LoadSup(1, "Smith, John", "Boston", 5);
  1080.  
  1081. tload returns 0 if no errors occurred.
  1082.  
  1083.  
  1084. Error -41
  1085.  
  1086. The most common error which can occur while loading or inserting rows
  1087. is error -41.  Error -41 occurs whenever you try to insert a row into
  1088. a table that already contains the key found in the row you are trying
  1089. to insert.  For example  
  1090.  
  1091.     i = tload(sups, 1, "Smith, John", "Boston", 5);  
  1092.     i = tload(sups, 1, "Hurt, Frank", "Denver", 10);
  1093.  
  1094.  
  1095.  
  1096.  
  1097.  
  1098.  
  1099.                      - 15 -
  1100.  
  1101. The first tload statement will return 0 meaning that the row was
  1102. successfully loaded (unless a supplier with an "s#" 1 already exists).
  1103. The second tload statement however will return -41, meaning that a row
  1104. with the same key (1) already exists and that the row was not loaded.
  1105. Unlike other error codes, -41 is not really considered to be a error
  1106. but rather an indicator that the primary key would be violated by
  1107. inserting the row.  -41 is also the only error code which will not
  1108. cause a function to abort.  Normally when an error occurs, a CRDE
  1109. function will immediately halt, do any cleaning up necessary and return.
  1110. Not so with error code -41.  This is why sometimes a function will
  1111. return successfully, but also set terrno to -41.  All that means is
  1112. that during the course of operation, the function tried to insert one
  1113. or more row(s) into a table which already contained a row(s) with the
  1114. same key.  
  1115.  
  1116.  
  1117. Inserting rows  
  1118.  
  1119. CRDE provides another function for inserting values into a table
  1120. called tinsert.  tinsert is declared in crde.h as
  1121.  
  1122.     long tinsert(TABLE *t, void *rows, long n);
  1123.  
  1124. tinsert expects rows to point to a buffer containing the row you want
  1125. to insert.  The buffer should be a structure defined exactly like the
  1126. table's table descriptor.  For example, to insert John Smith into the
  1127. sups table with tinsert would look like
  1128.  
  1129.     typedef struct {    
  1130.         int no;    
  1131.         char sname[26];    
  1132.         char city[26];    
  1133.         int status;  
  1134.     } sup;  
  1135.  
  1136.     sup s;  
  1137.  
  1138.     /* fill structure with values */
  1139.     s.no = 1;  
  1140.     strcpy(s.name, "Smith, John");  
  1141.     strcpy(s.city, "Boston");  
  1142.     s.status = 5;  
  1143.  
  1144.     /* insert structure into table */
  1145.     tinsert(sups, &s, 1);  
  1146.  
  1147. tinsert returns the number of rows inserted, in this case 1. You can
  1148. insert entire arrays into tables using tinsert.  rows should point to
  1149. the first element in the array.  n is the number of elements to insert.
  1150. To insert an array of 10 suppliers into the sups table would look like
  1151.  
  1152.     sup s[10];  
  1153.  
  1154.     /* fill s with info */  
  1155.     tinsert(sups, &s, 10);
  1156.  
  1157.  
  1158.  
  1159.                      - 16 -
  1160.  
  1161. Any rows in the array for which a key already exists in the table are
  1162. not inserted.  tinsert will return the number of rows that were
  1163. actually inserted into the table. 
  1164.  
  1165.  
  1166.  
  1167. Replacing rows
  1168.  
  1169. Normally when you try to insert a row into a table where a duplicate
  1170. key already exists, CRDE will not let you insert the row.  However,
  1171. sometimes you may want to throw out the existing row and replace it
  1172. with the new  row.  CRDE gives you this option to override this by
  1173. using the treplace function.  treplace is declared in crde.h as
  1174.  
  1175.     long treplace(TABLE *t, void *rows, long n);
  1176.  
  1177. treplace is identical to tinsert.  The only difference being that
  1178. tinsert inserts rows and treplace replaces them, which means if a row
  1179. with the same key as the row you are trying to insert already exists,
  1180. the old one is thrown out and the new one inserted.  If a duplicate
  1181. key does not exist, then treplace simply inserts the row into the table.
  1182.  
  1183. Note:  By default, tload always inserts rows. There is no complementary
  1184. function by which you can load values which replace rows.  If you want
  1185. to replace a row you must use treplace.
  1186.  
  1187.  
  1188. Write modes
  1189.  
  1190. Normally when you make call a CRDE function which alters a table, such
  1191. as tload, tinsert, or treplace, CRDE automatically writes those changes
  1192. to disk after every function call.  This is to protect data integrity.
  1193. If something were to go wrong (such as a power-outage) all your
  1194. information would be safely on disk.  In CRDE this is called normal
  1195. mode.  By default a table is in normal mode when it is opened or created.
  1196.  
  1197. However, there are some times when you may want to prevent CRDE from
  1198. instantly writing your changes to disk after every function call.  The
  1199. primary reason is speed.  Writing every change to disk as soon as it
  1200. occurs can take an enormous amount of time, especially if you are
  1201. making a lot of relatively small changes in succession.  That's why
  1202. CRDE has another mode, called write-cache, or writec,  mode.  If a
  1203. table is in this mode,  CRDE trys to keep as many changes buffered in
  1204. memory as possible, instead of writing them immediately to disk.
  1205.  
  1206. CRDE provides two functions for setting a table's mode to either normal
  1207. or write-cache. These are tnormal and twritec respectively.  twritec
  1208. puts a table in write-cache mode.  tnormal returns a table to normal
  1209. mode.  You usually use twritec to place a table in write-cache mode
  1210. when you are making "batch" changes, or several changes in succession.
  1211. For example, suppose you wanted to load the sups table with a bunch
  1212. of new suppliers,  
  1213.  
  1214.  
  1215.  
  1216.  
  1217.  
  1218.  
  1219.                      - 17 -
  1220.  
  1221.     twritec(sups);  
  1222.     tload(sups, 1, "Smith, John", "Boston", 5);  
  1223.     tload(sups, 2, "Jones, Ben", "New York", 10);  
  1224.     tload(sups, 3, "Barnes, Frank", "Boston", 15);  
  1225.     tload(sups, 4, "Landon, Mark", "Auburn", 5);  
  1226.     tload(sups, 5, "Andrews, Jim", "Rochester", 5);  
  1227.     /* load rest of suppliers here */  
  1228.     tnormal(sups);
  1229.  
  1230. tnormal returns the table to normal mode.  It also writes all changes
  1231. made while the table was in writec mode to disk.  You can explicitly
  1232. flush a table's unwritten buffers to disk at any without leaving writec
  1233. mode by using the tflush function.  Also, CRDE can flush a table's
  1234. buffers automatically if it needs more memory or if the buffers
  1235. become full.  You can use the tmode function to determine what mode a
  1236. table is currently in.  tmode will return one of four constants defined
  1237. in crde.h depending upon what mode the table is currently in.
  1238.  
  1239.     T_NORMAL        table is in normal mode.
  1240.     T_WRITEC        table is in write-cache mode.
  1241.     T_TEMP            table is a TEMP table.
  1242.     T_STATIC         table is a STATIC table.
  1243.  
  1244. T_TEMP and T_STATIC are only returned by specially designated temporary
  1245. tables which operate in their own special mode which is similar to
  1246. writec and cannot be changed.  Temporary tables are explained in detail
  1247. in Chapter 11.
  1248.  
  1249.  
  1250.  
  1251.  
  1252.  
  1253.  
  1254.  
  1255.  
  1256.  
  1257.  
  1258.  
  1259.  
  1260.  
  1261.  
  1262.  
  1263.  
  1264.  
  1265.  
  1266.  
  1267.  
  1268.  
  1269.  
  1270.  
  1271.  
  1272.  
  1273.  
  1274.  
  1275.  
  1276.  
  1277.  
  1278.  
  1279.                      - 18 -
  1280.  
  1281.  
  1282.  
  1283.  
  1284.  
  1285.  
  1286. Chapter 5
  1287.  
  1288. Basic Table I/O
  1289.  
  1290.  
  1291.  
  1292. Opening tables
  1293.  
  1294. This chapter describes some basic table operations such as opening
  1295. and closing tables, renaming tables, erasing tables, and indexing tables.
  1296. Once a table has been defined and exists on disk, you only need to open
  1297. it to use it again.  You open a table with the topen function.  For
  1298. example:  
  1299.  
  1300.     TABLE *t;  
  1301.  
  1302.     t = topen("sups");
  1303.  
  1304. topen is declared in crde.h as
  1305.  
  1306.     TABLE *topen(char *name);
  1307.  
  1308. topen expects the table's name as a parameter, which must be a legal DOS
  1309. pathname less than 64 characters (no wildcards or extension).  Once the
  1310. table is open, you can perform any operations on the table you wish.
  1311.  
  1312.  
  1313. Integrity checking
  1314.  
  1315. topen does a number of integrity checks to validate that the table is
  1316. not corrupt.  topen can detect almost any type of corruption in the
  1317. table, and will attempt to fix the problem if necessary.  The most
  1318. common type of corruption is an index which has become out of sync
  1319. with the table.  If topen detects this, it will automatically rebuild
  1320. the index. However, there are some things which even topen can't handle.
  1321. If topen cannot fix a damaged table, it will not open it, but return
  1322. an error code instead.  Your only alternative is to attempt to repair
  1323. the table with the trepair function.  Fixing damaged tables is
  1324. described in more detail in Chapter 15, Repairing Tables.
  1325.  
  1326.  
  1327. Closing a table
  1328.  
  1329. You should always close a table when you are finished with it. This will
  1330. insure that all unwritten data is written to disk and that any memory
  1331. that the table is holding is released.  You close a table with the
  1332. tclose function.  tclose is declared in crde.h as
  1333.  
  1334.     int tclose(TABLE *t);
  1335.  
  1336.  
  1337.  
  1338.  
  1339.                      - 19 -
  1340.  
  1341. For example, to close the above table, you would issue a
  1342.  
  1343.     tclose(t);
  1344.  
  1345.  
  1346. Checking a table's accessability
  1347.  
  1348. You can check if a table exists on disk with the taccess function.
  1349. taccess is declared in crde.h as
  1350.  
  1351.     int taccess(char *name, int mode);
  1352.  
  1353. name must be a valid table name.  You can check is a table exists by
  1354. calling taccess with mode set to 0.  taccess returns 0 if the table
  1355. exists or -1 if it does not.
  1356.  
  1357.  
  1358. Renaming tables
  1359.  
  1360. You can rename a table with the trename function.  trename renames the
  1361. table specified, including all indexes.  It is recommended that you use the
  1362. trename function whenever you want to rename a table.  trename is declared
  1363. in crde.h as  
  1364.  
  1365.     int trename(char *old, char *new);
  1366.  
  1367. trename renames table old to new.  old  and new must both be valid table
  1368. names.  trename will not overwrite a table by the name new if one already
  1369. exists.  If one does, trename will not rename the table.
  1370.  
  1371.  
  1372. Erasing tables
  1373.  
  1374. You can erase a table with the terase function.  terase erases the
  1375. specified table and all associated indexes. It is recommended that you
  1376. always use terase to erase a table. terase is declared in crde.h as
  1377.  
  1378.     int terase(char *name);
  1379.  
  1380. name must be a valid table name.
  1381.  
  1382.  
  1383. Secondary Indexes
  1384.  
  1385. Creating Indexes
  1386.  
  1387. An index, often called a secondary index in CRDE, is a special type of
  1388. file which can help CRDE perform very fast searches.  An index in CRDE,
  1389. is very much like an index in a book.  While the subjects in a book is
  1390. not in any particular order, the index is always maintained in
  1391. alphabetic order.  Whenever you want to find a particular subject in
  1392. the book, you look up its reference in the index.  This is very
  1393. similar to how indexes are used by CRDE.  You can create a secondary
  1394. index on a table with the tindex function.  tindex is declared in
  1395. crde.h as  
  1396.  
  1397.     int tindex(TABLE *t, char *id);
  1398.  
  1399.                      - 20 -
  1400.  
  1401. tindex returns 0 if the index was successfully created.  
  1402.  
  1403.  
  1404. Index descriptors 
  1405.  
  1406. id is a asciiz string which contains an index descriptor.  The index
  1407. descriptor is a list of all the columns which are part of the secondary
  1408. index.  Each column in the list must be separated by whitespace,
  1409. i.e. all characters classified by the standard library function
  1410. isspace() and the comma ','.  To create an index on the "sname" column
  1411. of the sups table would look like  
  1412.  
  1413.     tindex(sups, "sname");
  1414.  
  1415. In addition, any columns in the index which a prepended with an
  1416. exclaimation point '!' are indexed in descending order.  Which means a
  1417. valid index on the "sname" column would also be
  1418.  
  1419.     tindex(sups, "!sname");
  1420.  
  1421. In general however, you should avoid using descending keys in indexes.
  1422. Since it doesn't matter to CRDE whether an index key is in ascending
  1423. or descending order, just like it doesn't matter whether a telephone
  1424. book is ordered from A-Z or Z-A.  There is no advantage to using one
  1425. over the other, except in some rare occasions when CRDE requires that
  1426. the index be read sequentially.  This only really occurs in the tsort
  1427. function.  Keeping you indexes in ascending order will help avoid
  1428. confusion.
  1429.  
  1430. You can also create compound indexes by including more than one column
  1431. in the index descriptor.  Secondary indexes may contain up to 8 columns.
  1432. For example, to create a compound index on the "city" and "sname"
  1433. columns
  1434.  
  1435.     tindex(sups, "city, sname")
  1436.  
  1437. You can think of compound indexes as being ordered like words in a
  1438. dictionary.  Words in a dictionary are ordered by their first letter,
  1439. and then their second, and then their third, etc.  Similarly, suppliers
  1440. in the above index are ordered by city and those in the same city are
  1441. ordered by their supplier name.  Thus this index would be useful in
  1442. searches for suppliers in a particular city, or those in a particular
  1443. city with a particular name.  It would not, however, be useful for
  1444. finding just a particular name, just as it would be fruitless to
  1445. search for a word in a dictionary without knowing its first letter.
  1446.  
  1447. Once you create the index, you never have to remind CRDE to update or
  1448. use it.  CRDE does it for you automatically.  The index is now part of
  1449. the table and is opened and closed along with the table.  The index
  1450. remains part of the table until such time as it is dropped specifically
  1451. by you.  The advantage of secondary indexes speed.  On large tables,
  1452. indexes are especially attractive because search times for exact values
  1453. can be reduced to less than a second in most cases.  
  1454.  
  1455.  
  1456.  
  1457.  
  1458.  
  1459.                      - 21 -
  1460.  
  1461. However you should be sparing  when defining secondary indexes, only
  1462. using them when the increase in performance is significant enough to
  1463. warrant it.  While secondary indexes can increase search times, they
  1464. will slow other operations such as inserting, changing, and deleting
  1465. rows.  This is because CRDE must update the indexes whenever changes
  1466. are made to the table.  Secondary indexes also take up additional disk
  1467. space.  And sometimes creating a secondary index on a column(s) may
  1468. not always produce an increase in performance.  CRDE is exceptionally
  1469. fast at sequential searches.  Sometimes, on smaller tables, a
  1470. sequential search can be as fast as an indexed one.  CRDE may also
  1471. build a temporary index on its own when performing complex query
  1472. operations.  The best time to use a secondary index is on a large
  1473. table where you need to access exact values in a column.  For example,
  1474. you might want to place a secondary index on the "city" column if you
  1475. need to find a supplier who lives in Boston.   Poor choices for
  1476. secondary indexes are columns which contain lots of duplicate values,
  1477. f and $ columns, large c columns, columns which hold weights,
  1478. quantities, etc.
  1479.  
  1480.  
  1481. Primary index vs. Secondary Indexes
  1482.  
  1483. When you create a primary key, an index (the primary index) is created
  1484. which is similar to an index created by specifying those columns marked
  1485. with an asterisk '*' read from left to right in ascending order.  For
  1486. example, the primary index create on the orders table defined as
  1487.  
  1488.     orders = tcreat("orders", "*s# i, *p# i, qty i");
  1489.  
  1490. would create a primary index similar to a secondary index created by
  1491. the statement
  1492.  
  1493.     tindex(orders, "s#,p#");
  1494.  
  1495. For purposes of searching, sorting, and joining, etc. the two types of
  1496. indexes are functionally equivalent. Thus it would be redundant to
  1497. create the above secondary index on the orders table.
  1498.  
  1499. Despite the similarities, there are some basic differences between the
  1500. types of indexes:
  1501.  
  1502.     A primary index is created when the table is defined and cannot
  1503.     be removed afterwards.  Secondary indexes are created with the
  1504.     tindex function, and can be removed by tdropindex.
  1505.  
  1506.     A primary index cannot contain duplicate values, while a
  1507.     secondary index can.
  1508.  
  1509.  
  1510.  
  1511.  
  1512.  
  1513.  
  1514.  
  1515.  
  1516.  
  1517.  
  1518.  
  1519.                      - 22 -
  1520.  
  1521. Rebuilding indexes
  1522.  
  1523. You can also rebuild indexes on a table with the tindex function.
  1524. Simply specify the index to rebuild in the index descriptor.  Rebuilding
  1525. indexes is useful if you think that the index may be corrupt.  topen
  1526. will also rebuild an index automatically if it detects that the index
  1527. is corrupt.  
  1528.  
  1529.     tindex(sups, "sname");  /* create index */  
  1530.     /* ... */  
  1531.     tindex(sups, "sname");  /* rebuilds index */
  1532.  
  1533.  
  1534. Dropping indexes
  1535.  
  1536. You can drop indexes from a table by using the tdropindex function. 
  1537. tdropindex is declared in crde.h as  
  1538.  
  1539.     int tdropindex(TABLE *t, char *id);
  1540.  
  1541. tdropindex will drop the index exactly matching the index descriptor
  1542. specified.  To drop the "sname" index from a table would look like
  1543.  
  1544.     tdropindex(sups, "sname");
  1545.  
  1546.  
  1547. Redundant indexes
  1548.  
  1549. It is possible when creating several secondary indexes, that two or
  1550. more indexes may be redundant.  An obvious example is shown above:
  1551. creating a secondary index identical to the primary index.  There
  1552. are other possibilities for redundancy.  CRDE prevents you from
  1553. creating duplicate secondary indexes because whenever you do so,
  1554. tindex simply rebuilds the original.  However, it does not prevent
  1555. you from creating redundant indexes.  You must do this on your own.
  1556.  
  1557. Redundant indexes are those which can be derived from, or used in
  1558. place by, existing indexes.  One example of a redundant index is
  1559. creating a secondary index identical to the primary index.  Here are
  1560. some other examples of redundant indexes:
  1561.  
  1562.     /* the second index is simply an inverse of the first */
  1563.     tindex("sname");
  1564.     tindex("!sname");
  1565.  
  1566.     /* the second index is simply an inverse of the first */
  1567.     tindex("s#, !city");
  1568.     tindex("!s#, city");  
  1569.  
  1570.     /* the second two indexes are just more generals versions of the
  1571.     first */
  1572.     tindex("city,sname,status");
  1573.     tindex("city,sname");
  1574.     tindex("city");
  1575.  
  1576.  
  1577.  
  1578.  
  1579.                      - 23 -
  1580.  
  1581.  
  1582.  
  1583.  
  1584.  
  1585.  
  1586. Chapter 6
  1587.  
  1588. Viewing a Table
  1589.  
  1590.  
  1591.  
  1592. The tview function
  1593.  
  1594. CRDE provides an extremely powerful view facility which you can integrate
  1595. directly into your programs.  With a single function call you can  
  1596.     
  1597.     - view any CRDE table in any text window on your monitor
  1598.     with your choice of color for text, frame, and background.  The
  1599.     table is automatically adjusted to the window and  you can
  1600.     scroll both horizontally and vertically through a table.
  1601.  
  1602.     - view only those columns you want to see, in any order you
  1603.     wish.
  1604.  
  1605.     - view the rows in any sorted order, or in the order they were
  1606.     originally inserted into the table.
  1607.  
  1608.     - create a pick list to select rows from a table.
  1609.  
  1610. CRDE's view function is called tview.  tview is declared in crde.h as
  1611.  
  1612.     int tview(TABLE *t, char *cl, char *id, int x1, int y1, int x2,
  1613.       int y1, int cattr, int fattr, int battr, void *rec);
  1614.  
  1615. t is the table to be viewed.  t must point to an open table.  
  1616.  
  1617. cl is an asciiz string listing all the columns you want to view.  Each
  1618. column name must be separated by whitespace.  You may order the columns
  1619. in any order you wish.  If cl is NULL then every column will be
  1620. displayed in the order they appear in the table's table descriptor.
  1621. id is an index descriptor describing the order in which the rows in
  1622. the table will appear.  If id is NULL the rows will  appear in the
  1623. order they were originally inserted into the table.
  1624.  
  1625. x1,y1,x2,y2 are the coordinates of the window the view will appear in.
  1626. The coordinates can range from 1,1 to 80,25.  But the window must have
  1627. a minimum width of 5 and minimum height of 1.  Some columns, such as
  1628. large c columns, may be too wide to fit in the specified window.
  1629. If so, then the column will be truncated so that it fits.
  1630.  
  1631. cattr is the text attr for all values in the columns.
  1632.  
  1633. fattr is the text attr for the view frame.
  1634.  
  1635.  
  1636.  
  1637.  
  1638.  
  1639.                      - 24 -
  1640.  
  1641. battr is the text attr for any portion of the window not filled by the
  1642. view.  
  1643.  
  1644. rec should point to a buffer large enough to hold one row in the table.
  1645. When you exit the tview function, the row that the cursor was last on
  1646. will be placed into rec.  You can use this feature to use tview as a
  1647. pick list to select a row from a table.  You can keep tview from
  1648. copying a row into the buffer by assigning rec to NULL.
  1649.  
  1650. tview offers a full range of movement up, down, left, right, beginning,
  1651. end. If the table is too large to fit in the specified window, tview
  1652. can scroll the table both horizontally and vertically so that you can
  1653. see every portion of the table.  
  1654.  
  1655.     Cursor key          Action
  1656.   
  1657.     Up                   Move up a row.  
  1658.     Down                 Move down a row.  
  1659.     Left                 Move left one column.  
  1660.     Right               Move right one column.  
  1661.     PgUp                 Move up a page.  
  1662.     PgDn                 Move down a page.  
  1663.     Home                 Move to the first row.
  1664.     End                  Move to the last row.
  1665.     Ctrl-Left            Move to the first column.
  1666.     Ctrl-Right           Move to the last column.
  1667.  
  1668. You can exit from tview by pressing either the Enter key or the Esc key.
  1669. Which key you hit determines what value is returned by tview. This is
  1670. particularly valuable when using tview as a pick list.  You might use
  1671. tview to select a specific row then process the row if the user
  1672. presses Enter, or cancel the operation if Esc is pressed.
  1673.  
  1674.     Enter           Exit tview returning 13.
  1675.     Esc                Exit tview returning 27.
  1676.  
  1677. Selecting which columns you want to view can be an invaluable feature
  1678. when you want to restrict the information a particular user can see.
  1679. For example, you might want to let a user view an employees table
  1680. without letting them see the employee's salary.  tview always displays
  1681. the current row number in the leftmost column, regardless of which
  1682. columns you decide to display.
  1683.  
  1684. Selecting the sort order you want the table to be displayed in can be
  1685. invaluable as well.  However, when viewing very large tables (+5000
  1686. rows) in sorted order, tview can slow down dramtically.  Its usually
  1687. best to view large tables in the order in which they were inserted.
  1688.  
  1689.  
  1690. Limitations of tview
  1691.  
  1692. tview can only be used for viewing tables.  You cannot insert, update,
  1693. or delete rows from a table with tview.  tview adds about 10k to your
  1694. programs.
  1695.  
  1696.  
  1697.  
  1698.  
  1699.                      - 25 -
  1700.  
  1701.  
  1702.  
  1703.  
  1704.  
  1705.  
  1706. Chapter 7
  1707.  
  1708. The Search-Related Functions
  1709.  
  1710.  
  1711.  
  1712. The seven search-related functions
  1713.  
  1714. The CRDE library contains seven special functions classified as the
  1715. search-related functions.  These functions allow you to perform actions
  1716. on subsets of a table simply by specifying the rows that you want
  1717. to perform the operation on. The seven search-related functions are
  1718.  
  1719.     tselect         selects rows from a table  
  1720.     tproject        projects a column from the table  
  1721.     tdelete         deletes rows from a table  
  1722.     tchange         changes rows in a table  
  1723.     tscan           scans rows in a table  
  1724.     tlookup        looks up a row in a table  
  1725.     tget        gets rows from a table into a C array
  1726.  
  1727.  
  1728. The search expression
  1729.  
  1730. Every search related function expects a NULL terminated list of
  1731. arguments which formulate a search expression.  The search-related
  1732. function will only perform its operation on those rows which match
  1733. the criteria in the search expression.  Lets start with an example:
  1734. tselect.  tselect is declared in crde.h as  
  1735.  
  1736.     TABLE *tselect(char *name, TABLE *t, ...);
  1737.  
  1738. Every search related function's parameter list ends with an
  1739. ellipsis(...).  A call to tselect will have the format
  1740.  
  1741.     answer = tselect(name, t, colname1, relop1, value1, colname2,
  1742.       relop2, value2, ..., NULL);
  1743.  
  1744. A search expression is made up of zero or more conditions followed by
  1745. a terminating NULL.  A condition is a colname[n], relop[n], value[n]
  1746. combination.   
  1747.  
  1748. colname[n] must be a column name from table t.   
  1749.  
  1750. relop[n] is one of six relational operators defined in crde.h.  Valid
  1751. relops are:     
  1752.  
  1753.  
  1754.  
  1755.  
  1756.  
  1757.  
  1758.  
  1759.                      - 26 -
  1760.  
  1761.     EQ           equals, ==
  1762.     GT           greater than, >
  1763.     LT           less than, <
  1764.     LE, LTE       less than or equal to, <=
  1765.     GT, GTE       greater than or equal to, >=
  1766.     NE, NEQ       not equal to, !=
  1767.  
  1768. value[n] must be an argument which evaluates to a constant.  The type
  1769. of the argument must be identical to the type of colname[n] (c columns
  1770. expect a char * as a value).  For example, suppose you wanted a list
  1771. of all suppliers who live in Boston.  You could accomplish this with
  1772. the following call:  
  1773.  
  1774.     answer = tselect("answer", sups, "city", EQ, "Boston", NULL);
  1775.  
  1776. tselect returns a table as a result.  The structure and primary key
  1777. of the created table are identical to the table being queried, in
  1778. this case sups.  tselect creates a table called answer identical in
  1779. structure to sups containing all the rows from sups where the "city"
  1780. column equals "Boston".  Notice the terminating NULL.  Every search
  1781. expression must end with a NULL.  
  1782.  
  1783. A search expression may contain up to 16 conditions.  A search
  1784. expression may also contain no conditions.  In this case, the search
  1785. expression would only consist of the terminating NULL.  A search
  1786. expression with no conditions will select every row from the table.
  1787.  
  1788.  
  1789. The search engine
  1790.  
  1791. CRDE handles searches with its search engine.  The search engine
  1792. parses the search expression and determines the fastest way to perform
  1793. the search, regardless of how the search expression is specified.
  1794. The search engine knows about your indexes and will use them
  1795. automatically to assist in the search.
  1796.  
  1797.  
  1798. Search-related operations
  1799.  
  1800. Selecting rows
  1801.  
  1802. tselect is one of several functions which returns a table as a result.
  1803. tselect returns a table identical in structure as t, containing all
  1804. the rows from t matching the search expression.
  1805.  
  1806.     answer = tselect("answer", sups, "city", EQ, "Boston", NULL);
  1807.  
  1808. answer contains all the rows from sups where the supplier lives in
  1809. Boston.  The structure of the answer table is identical to sups.
  1810. It has the same columns and column types.  It also has the same
  1811. primary key.  It will not have any secondary keys,  even if sups
  1812. does.  Although CRDE considers secondary keys to be part of a table,
  1813. they are not considered part of the table's structure.  
  1814.  
  1815.  
  1816.  
  1817.  
  1818.  
  1819.                      - 27 -
  1820.  
  1821. Projecting rows
  1822.  
  1823. Projecting columns from a table is similar to selecting them, except
  1824. you have the option to choose only those columns you want to appear
  1825. in the answer table.  You can project columns from a table with the
  1826. tproject function.  tproject is declared in crde.h as
  1827.  
  1828.     TABLE *tproject(char *name, TABLE *t, char *pl, ...);
  1829.  
  1830. The pl parameter is an asciiz string containing a list of those columns
  1831. you want to appear in the resulting table.  You can select only those
  1832. columns you want, in any order that you want. For example, to select
  1833. only a supplier's name and city from the sups table would look like
  1834.  
  1835.     answer = tproject("answer", sups, "name,city", NULL);
  1836.  
  1837. Notice there are no conditions in the search expression, just the
  1838. terminating NULL.  A search expression with no conditions selects
  1839. every row in the table.  If you wanted the city column to appear
  1840. first, just switch them around 
  1841.  
  1842.     answer = tproject("answer", sups, "city,name", NULL);
  1843.  
  1844. Of course, you could also project the name and city columns from only
  1845. those suppliers in Boston with
  1846.  
  1847.     answer = tproject("answer", sups, "name,city", "city", EQ, "Boston",
  1848.       NULL);
  1849.  
  1850. The structure of the new table will be identical to the columns listed
  1851. in pl in the same order they appear in pl. The type of each column is
  1852. identical to its type in t.
  1853.  
  1854.  
  1855. Defining a primary key with tproject
  1856.  
  1857. The above query create table a table with two columns which has a
  1858. structure identical to "name c26, city c26".  The table has no
  1859. primary key.  You can specify a primary key on the projected table
  1860. by prepending any columns with an asterisk '*'.  Like in a table
  1861. descriptor, a maximum of 8 columns may be part of the primary key.
  1862. The projected table by default does not inherit a primary key unless
  1863. it is specifically specified.  For example, if you projected the "s#"
  1864. column from sups, the resulting table would not inherit the primary
  1865. key of "s#" unless you specifically used "*s#".  Defining a primary
  1866. key on the projected table can very useful, especially in removing
  1867. duplicate values from the result table.  For example suppose you
  1868. wanted a list of all cities where there are suppliers.  Defining a
  1869. query like  
  1870.  
  1871.     answer = tproject("answer", sups, "city", NULL);
  1872.  
  1873.  
  1874.  
  1875.  
  1876.  
  1877.  
  1878.  
  1879.                      - 28 -
  1880.  
  1881. might not have the exact effect you would like.  Why? because if more
  1882. than one supplier lived in the same city, that city would appear more
  1883. than once in the list.  If more than one supplier lives in the same
  1884. city, you only want that city to appear once.
  1885.  
  1886. You can get the result you want by defining a primary key on the
  1887. "city" column.
  1888.  
  1889.     answer = tproject("answer", sups, "*city", NULL);
  1890.  
  1891. The resulting table will have a primary key on the "city" column.
  1892. Because, by definition, no two rows in a table may have the same
  1893. key values, CRDE will automatically filter out any duplicates.
  1894. Similarly, tproject can be used to change or remove the primary key
  1895. on a table.  For example, if we wanted to remove the primary key from
  1896. the sups table you could do this with
  1897.  
  1898.     answer = tproject("answer", sups, "s#,sname,city,status", NULL);
  1899.  
  1900. answer would be a copy of sups without a primary key.
  1901.  
  1902.  
  1903. Deleting rows
  1904.  
  1905. You can delete rows from a table with the tdelete function.  tdelete
  1906. is declared in crde.h as
  1907.  
  1908.     long tdelete(t, ...);
  1909.  
  1910. tdelete is fairly straightforward.  All rows matching the search
  1911. expression are deleted from the table.  For example, to delete
  1912. supplier #5 from the sups table  
  1913.  
  1914.     tdelete(sups, "s#", EQ, 5, NULL);
  1915.  
  1916. tdelete returns the number of rows deleted.  
  1917.  
  1918.  
  1919. How rows are deleted
  1920.  
  1921. tdelete doesn't physically delete rows from a table.  Rather it marks
  1922. the space as unused.  The space is reused when you re-insert rows into
  1923. the table.  Because CRDE reuses the space in tables caused whenever a
  1924. deletion occurs, you do not have to "pack" your tables like you do in
  1925. other data managers.  A special case is when you want to remove all
  1926. rows from a table.  This is usually referred to as emptying a table.
  1927. You can empty a table with tdelete by issuing a
  1928.  
  1929.     tdelete(t, NULL);
  1930.  
  1931. However, this would leave the entire table unused and waste a lot of
  1932. disk space.  CRDE provides a function to remove all the rows from a
  1933. table, which does physically reclaim any unused space, called tempty.
  1934. tempty is declared in crde.h as
  1935.  
  1936.     tempty(TABLE *t);
  1937.  
  1938.  
  1939.                      - 29 -
  1940.  
  1941. You should always use tempty when you want to remove every row from a
  1942. table for this reason.  Using tempty is also much quicker than the
  1943. tdelete method.
  1944.  
  1945.  
  1946. Changing rows
  1947.  
  1948. You can change values in a table by using the tchange function.  The
  1949. tchange function is declared in crde.h as
  1950.  
  1951.     long tchange(TABLE *t, ...);
  1952.  
  1953. tchange returns the number of rows changed.  tchange is little
  1954. different from the other search-related functions.  It expects not one,
  1955. but two NULL terminated lists of arguments.  The first is a list of
  1956. changes, followed by the standard search expression.  A sample call
  1957. to tchange will have the format 
  1958.  
  1959.     tchange(t, chcol1,chval1, chcol2, chval2, ..., NULL, colname1,
  1960.       relop1, value1, colname2, relop2, value2, ..., NULL);
  1961.  
  1962. A change list is made up to 16 changes.  A change consists of a
  1963. colname[n], chcol[n] combination.
  1964.  
  1965. chcol[n] is the column name of the column to be changed.  
  1966.  
  1967. chval[n] is the value that the column will be set to in every row which
  1968. matches the search expression.  chval[n] must resolve  can be a
  1969. constant, variable or function result which has a type identical to
  1970. chcol[n]. c columns expect a type char *.
  1971.  
  1972. For example, suppose you want to change part #3's color to purple.
  1973. You could do this with a simple tchange call
  1974.  
  1975.     tchange(parts, "color", "purple", NULL, "p#", EQ, 3, NULL);
  1976.  
  1977. tchange can be used to make global changes.  For example, at the end
  1978. of a year you might want to reset every supplier's status to 0. To do
  1979. this with tchange would look like
  1980.  
  1981.     tchange(sups, "status", 0, NULL, NULL);
  1982.  
  1983. A common error made with tchange is that you must remember to specify
  1984. two NULLs instead of just one.
  1985.  
  1986.  
  1987. Scanning rows
  1988.  
  1989. You can count the number of rows which match a search expression by
  1990. using the tscan function.  tscan is declared in crde.h as
  1991.  
  1992.     long tscan(TABLE *t, ...);
  1993.  
  1994. tscan returns the number of rows scanned.
  1995.  
  1996.  
  1997.  
  1998.  
  1999.                      - 30 -
  2000.  
  2001. tscan really doesn't do anything accept count the number of rows which
  2002. match the search expression.  tscan is not usually used much in
  2003. applications.
  2004.  
  2005.  
  2006. Looking up a row
  2007.  
  2008. You can determine if a row matching a given search expression exists
  2009. with the tlookup function.  The tlookup function is declared in crde.h
  2010. as  
  2011.  
  2012.     int tlookup(TABLE *t, ...);
  2013.  
  2014. tlookup returns 1 if a row matching the search expression was found or
  2015. 0 if it was not.  tlookup stops searching after the first match is found.
  2016.  
  2017.  
  2018. Getting rows
  2019.  
  2020. tget is similar to tselect, except that the rows that are selected are
  2021. placed in a C array rather than a table.  tget is declared in crde.h as
  2022.  
  2023.     long tget(TABLE *t, void *array, long n, ...);
  2024.  
  2025. tget places up to n rows that match the search expression into the
  2026. buffer pointed to by array.  The buffer should be an array of at least
  2027. n structures identical the structure of the table t.  It is important
  2028. to remember that the rows that are extracted with tget are only copies
  2029. of the rows in the table.  The rows retrieved by tget are not removed
  2030. from the table, nor will changing the rows in the array have any affect
  2031. on the original values in the table.  
  2032.  
  2033. The following exmaple places every supplier with a status > 5 into an
  2034. array buf.
  2035.  
  2036.  
  2037.     typedef struct {
  2038.         int s;
  2039.         char sname[26];
  2040.         char city[26];
  2041.         int status;
  2042.     } sup;
  2043.  
  2044.     sup buf[20];
  2045.     int i;
  2046.  
  2047.     i = tget(sups,  buf, 20, "status", GT, 5, NULL);
  2048.  
  2049. tget returns the number of rows actually retreived, which in this case
  2050. will be between 0 and 20.
  2051.  
  2052. tget is often used in conjunction with treplace.  You can get rows
  2053. from a table with tget, perform operations on them, and return them to
  2054. the table with treplace.
  2055.  
  2056.  
  2057.  
  2058.  
  2059.                      - 31 -
  2060.  
  2061. "..If" functions
  2062.  
  2063. Although a search expression is capable of handling many searches, it
  2064. cannot handle all of them.  Fortunately, CRDE provides a version of each
  2065. of the above functions which allow unlimited searching capabilities.
  2066. These are referred to as "..if" functions.  They are, respectively
  2067.  
  2068.     tselectif  
  2069.     tprojectif  
  2070.     tdeleteif  
  2071.     tchangeif 
  2072.     tscanif  
  2073.     tlookupif 
  2074.     tgetif
  2075.  
  2076. Each is identical in use and declaration to its brother function in
  2077. every way except one.  Each excepts an additional parameter which can
  2078. be used as a filter to create user-defined search conditions.  In each
  2079. declaration, the user-defined function action, appears directly before
  2080. the ellipsis in a search related function.  For example, tselectif
  2081. is declared as  
  2082.  
  2083.     TABLE *tselectif(char *name, TABLE *t, int (*action)(void *), ...);
  2084.  
  2085. action is a user-defined function which must accept a void * as a
  2086. parameter and return an int.  Every row which matches the search
  2087. expression is passed to action (which is referenced by void *) for
  2088. further evaluation.  The result of action determines whether or not
  2089. an operation will occur.  
  2090.  
  2091.     Result of action           What happens
  2092.  
  2093.         > 0              Row is selected.    
  2094.  
  2095.         = 0              Row is not selected.    
  2096.  
  2097.         < 0              The function automatically aborts as
  2098.                 if an error occurred.  If the "..if"
  2099.                 function returns a TABLE * then it will
  2100.                 return NULL otherwise the result of
  2101.                 action is returned by the function.
  2102.                 terrno is set to the result of action.
  2103.  
  2104. By using "..if" functions, programs can create virtually any search
  2105. criteria.  For example, suppose you wanted to select all suppliers
  2106. who either live in Boston or Denver.  You cannot create this sort of
  2107. search expression because only those rows which match every condition
  2108. in the expression are selected.   Specifying 
  2109.  
  2110.     answer = tselect("answer", sups, "city", EQ, "Boston", "city",
  2111.       EQ, "Denver");
  2112.  
  2113. would not answer the question, because CRDE would try to select every
  2114. supplier would lives in Boston and Denver, an impossibility.  This
  2115. problem however, is easily handled with a simple "if" function.
  2116.  
  2117.  
  2118.  
  2119.                      - 32 -
  2120.  
  2121. Example #1.  
  2122.  
  2123.     typedef struct {    
  2124.         int no;    
  2125.         char sname[26];    
  2126.         char city[26];    
  2127.         int status;  
  2128.     } sup;  
  2129.  
  2130.     int iffunc(void *rec)  {    
  2131.       return strcmp(((sup *)rec)->city, "Boston") == 0 ||
  2132.         strcmp(((sup*)rec)->city, "Denver") == 0;
  2133.     }
  2134.  
  2135.     answer = tselectif("answer", sups, iffunc, NULL);
  2136.  
  2137. No search expression is supplied, so tselectif passes every row in the
  2138. table to iffunc for evaluation.  iffunc returns true if the city field
  2139. equals Boston or Denver.  Notice that since rec must be a void *, you
  2140. must typecast it to extract data from the row.
  2141.  
  2142.  
  2143. Example #2.
  2144.  
  2145. Select all parts which are red ignoring any case differences.  
  2146.  
  2147.     int ColorIsRed(void *rec)  {    
  2148.         return stricmp(((part *)rec)->color, "RED");  
  2149.     }
  2150.  
  2151.     answer = tselectif("answer", parts, ColorIsRed, NULL);
  2152.  
  2153.  
  2154. Example #3.
  2155.  
  2156. Change all the part's colors to uppercase.
  2157.  
  2158. The user-defined function for tchangeif works a little differently
  2159. than in other "..if" functions.  Any changes made to the row passed
  2160. to action are reflected in the table (if, of course, action returns > 0).
  2161. Thus tchangeif allows you to perform user-defined changes as well as
  2162. user-defined selection.  For example, to change every part's color to
  2163. uppercase would look something like 
  2164.  
  2165.     int ColorUpr(void *rec)  {    
  2166.         strupr(((part *)rec)->color));    
  2167.         return 1; /* change every row */  
  2168.     }  
  2169.  
  2170.     tchangeif(parts, CityUpr, NULL, NULL);
  2171.  
  2172.  
  2173.  
  2174.  
  2175.  
  2176.  
  2177.  
  2178.  
  2179.                      - 33 -
  2180.  
  2181. The NULLs are required even when no change list or search expression
  2182. is specified.  Note:  any changes made in the change list are made after
  2183. the row is passed to action.  action may modify the record passed to
  2184. it without harm (although this does have side effects when used in
  2185. tchangeif).  action can perform operations on other tables based on
  2186. the information passed to it.  action can even perform queries on the
  2187. host table (the table whose rows are being passed to action).  This
  2188. allows for extremely powerful and complex querying capabilities.
  2189. However, there is one restriction.  action is disallowed from altering
  2190. t in any way.  When any "..if function" calls action, t is automatically
  2191. placed in a special read-only mode.  Because of the complex nature in
  2192. which CRDE performs searches, t cannot be changed during the course of
  2193. a query.  CRDE functions which attempt to alter t will return a -152,
  2194. or "table may not be modified", error.  CRDE functions which can
  2195. change a table are listed below:   
  2196.  
  2197.     tadd        tchange        tchangeif    tchcol
  2198.     tdelete        tdeleteif     tdropindex     tempty
  2199.     tindex         tinsert        tload          treplace
  2200.        tsubtract      tupdate
  2201.  
  2202. In addition, you may not close or drop table in read-only mode.  This
  2203. is also the only case in which if t is a TEMP table, it will not be
  2204. automatically dropped by any CRDE functions called in action (TEMP
  2205. tables and auto-cleanup are described in full detail in Chapter 10).
  2206.  
  2207. The following example, although meaningless, demonstrates the abilities
  2208. and limitations within an if function.
  2209.  
  2210.     TABLE *sups, *answer;
  2211.  
  2212.     int  iffunc(void *rec)
  2213.     {
  2214.       TABLE *t;
  2215.       double d;
  2216.  
  2217.       /* these function calls are ok, do not change sups */
  2218.       t = tselect("test", sups, "s#", EQ, 100, NULL);
  2219.       tdrop(t);
  2220.       taverage(sups, "status", &d);
  2221.  
  2222.       /* illegal to change sups within iffunc */
  2223.       t = tdelete(sups, "status", GT, 20, NULL);
  2224.  
  2225.       /* cannot close sups within iffunc */
  2226.       tclose(sups);
  2227.       return 1;
  2228.     }
  2229.  
  2230.     answer = tselectif("answer", t, iffunc, NULL);
  2231.  
  2232.  
  2233.  
  2234.  
  2235.  
  2236.  
  2237.  
  2238.  
  2239.                      - 34 -
  2240.  
  2241. As you can see, "..if" functions provide almost unlimited querying
  2242. capabilities.  You might think the these functions could replace
  2243. their regular counter parts because you can do everything a search
  2244. expression can do inside of action.  However, there are several
  2245. reasons why not to:  
  2246.  
  2247.     - the most obvious is that "..if" functions require you to write
  2248.     a user-defined function for every query.
  2249.  
  2250.     - CRDE can only take advantage your indexes if you specify a
  2251.     search expression. For this reason you should always put as
  2252.     much of the query    in the search expression as you possibly
  2253.     can.
  2254.  
  2255.     - and since action must receive a copy of every record for
  2256.     evaluation, "..if functions" are slightly slower than their
  2257.     regular counterparts.
  2258.  
  2259. Some queries, however, cannot be performed without an "..if" function.
  2260. The full capabilities of "..if" functions are demonstrated in Chapter
  2261. 11, Performing Queries.
  2262.  
  2263.  
  2264.  
  2265.  
  2266.  
  2267.  
  2268.  
  2269.  
  2270.  
  2271.  
  2272.  
  2273.  
  2274.  
  2275.  
  2276.  
  2277.  
  2278.  
  2279.  
  2280.  
  2281.  
  2282.  
  2283.  
  2284.  
  2285.  
  2286.  
  2287.  
  2288.  
  2289.  
  2290.  
  2291.  
  2292.  
  2293.  
  2294.  
  2295.  
  2296.  
  2297.  
  2298.  
  2299.                      - 35 -
  2300.  
  2301.  
  2302.  
  2303.  
  2304.  
  2305.  
  2306. Chapter 8
  2307.  
  2308. Miscelleneous Operations
  2309.  
  2310.  
  2311.  
  2312. Restructuring tables
  2313.  
  2314. Like most data management systems (good ones anyway), CRDE provides
  2315. a way to restructure tables, without having to unload and reload all
  2316. the table's information.
  2317.  
  2318. Unlike other libraries which require you to use a special utility to
  2319. restructure a table, CRDE provides the trestruct function.  With the
  2320. trestruct function you can
  2321.  
  2322.     - add columns to a table 
  2323.     - remove columns from a table 
  2324.     - reorder columns in a table  
  2325.     - change column types and sizes  
  2326.     - change the primary key
  2327.  
  2328. trestruct is declared in crde.h as  
  2329.  
  2330.     TABLE *trestruct(char *name, TABLE *t, char *td);
  2331.  
  2332. trestruct returns a restructed version of t based upon td.  td should
  2333. be a table descriptor like the one described in Chapter 3.  trestruct
  2334. interprets the columns in td as follows:
  2335.  
  2336.     - If the column name does not appear in t then it is added to
  2337.       the table and filled with some default value (like 0 or "")
  2338.       depending upon the datatype.
  2339.  
  2340.     - If the column does appear in t and has the same type, then all
  2341.       data from the original column is copied into the new column.
  2342.  
  2343.     - If the column appears in t but has a different type, trestruct
  2344.       converts all data in the original column to the type of the new
  2345.       column.
  2346.  
  2347.     - Any columns in the original table t, which do not appear in
  2348.       td are not included in the restructured table.
  2349.  
  2350.  
  2351.  
  2352.  
  2353.  
  2354.  
  2355.  
  2356.  
  2357.  
  2358.  
  2359.                      - 36 -
  2360.  
  2361. Data conversion
  2362.  
  2363. trestruct can convert any data type to any other data type except for
  2364. dates.  Date columns can only be converted to char columns and only
  2365. char columns can be converted into dates.  When converting data to
  2366. char columns, trestruct converts the value  into a string and copying
  2367. it into the new column.  f columns will have 4 decimal places, $
  2368. will have 2. Dates are formatted as  "mm/dd/yyyy".  If the string is
  2369. too large to fit in the new column  it will be truncated automatically.
  2370. When converting char types to other data types, the char column must
  2371. hold be valid representation of the new value.  If it isn't then the
  2372. column will be initialized as described above.  Data converted to f
  2373. and $ columns may contain any number of decimal places.  Data
  2374. converted to dates must have the format "mm/dd/yyyy".  If a value
  2375. being converted to a numeric data type ( i, l, f, $ )  is out of
  2376. range for that type, the converted value is undefined.  
  2377.  
  2378. For following example uses trestruct to create a new sups table:
  2379.  
  2380.     t = trestruct("restruct", sups, "s# l, sname c16, city c16,"
  2381.       "first d");
  2382.  
  2383.     - "s#" is converted to type l.
  2384.     - "sname" and "city" are shortened.
  2385.     - "status" does not appear in the table descriptor and so is
  2386.       not included in the new table.
  2387.     - The "first" column is added to the table.  It contains the
  2388.       date the first order was placed to that supplier.
  2389.  
  2390. trestruct returns a restructed copy of the sups table.  sups is still
  2391. intact.  To replace sups with the new table, you would have to erase
  2392. the old sups and replace it with the new table.
  2393.  
  2394.     tdrop(sups);
  2395.     tclose(t);
  2396.     trename("restruct", "sups");
  2397.  
  2398.  
  2399. Renaming columns
  2400.  
  2401. You can change a column's name with the tchcol function.  tchcol is
  2402. declared in crde.h as
  2403.  
  2404.     int tchcol(TABLE *t, char *old, char *new);
  2405.  
  2406. tchcol renames column old to new.  The type or size of the column remains
  2407. unchanged.  
  2408.  
  2409.  
  2410.  
  2411.  
  2412.  
  2413.  
  2414.  
  2415.  
  2416.  
  2417.  
  2418.  
  2419.                      - 37 -
  2420.  
  2421. Getting info from your tables
  2422.  
  2423. CRDE provides several functions for getting information about the
  2424. structure and properties of a table.
  2425.  
  2426.     trows        Returns the number of rows in a table.
  2427.     tcols           Returns the number of columns in a table.
  2428.     tcoltype       Returns the type of a column.
  2429.     tcolsize       Returns the size of a column, in bytes.
  2430.     trowsize      Returns the size of a row, in bytes.
  2431.     tindexes       Returns the number of secondary indexes on a
  2432.             table.
  2433.     tkeyed         Returns 1 if the table has a primary key or 0
  2434.             if not.
  2435.  
  2436.     tstruct       Returns a table containing information about the
  2437.             structure of a table.
  2438.  
  2439.     tkeys          Returns a table containing information about the
  2440.             secondary indexes on a table.
  2441.  
  2442. Of these, only tstruct and tkeys bear much detail.  The others are
  2443. relatively self-explanatory.  tstruct is declared in crde.h as
  2444.  
  2445.     TABLE *tstruct(char *name, TABLE *t);
  2446.  
  2447. tstruct returns the structure of a table, as a table.  The result is a
  2448. table which is equivalent to
  2449.  
  2450.     tcreat(name, "name c18,type c6").  
  2451.  
  2452. Column names and types appear in the struct table in the order that
  2453. they were declared.  Columns which are part of the primary key have
  2454. their names prepended with an asterisk '*'.  Using tstruct on the sups
  2455. table would produce the following table:
  2456.  
  2457.     name             type   
  2458.     -----------------------    ------
  2459.     *s#            i
  2460.     sname            c26
  2461.     city            c26
  2462.     status            i
  2463.  
  2464.  
  2465. Altering the table produced by tstruct has absolutely no effect upon the
  2466. structure of the table passed to tstruct.
  2467.  
  2468. tkeys is a similar function for obtaining information about a table'
  2469. s secondary indexes.  tkeys produces a table equivalent to
  2470.  
  2471.     tcreat(name,"ext c3, key c144"). 
  2472.  
  2473.  
  2474.  
  2475.  
  2476.  
  2477.  
  2478.  
  2479.                      - 38 -
  2480.  
  2481. The ext column contain the indexes' extension on disk,  "s0" - "s7".
  2482. The key column contains the index descriptor which created the
  2483. secondary index.  To illustrate, suppose the sups table had a two
  2484. secondary indexes created by
  2485.  
  2486.     tindex(sups, "sname");
  2487.     tindex(sups, "city,status");
  2488.     
  2489. Calling tkeys on sups would return the table 
  2490.  
  2491.     ext    key
  2492.     -------    -------------------------
  2493.     s0    sname
  2494.     s1    city,status
  2495.  
  2496.  
  2497. Importing and Exporting data
  2498.  
  2499. CRDE provides functions for importing and exporting data to other
  2500. file formats.  CRDE currently supports the ascii and dBASE file formats.
  2501. Since most other data managers support either one or both of these
  2502. types, you should have no problem converting CRDE tables to and from
  2503. virtually any other format.
  2504.  
  2505.  
  2506. Ascii files.
  2507.  
  2508. CRDE supports comma-delimited ascii compatible files.  CRDE can import
  2509. an ascii file with its timportascii function.  timportascii is
  2510. declared in crde.h as
  2511.  
  2512.     TABLE *timportascii(char *name, char *td, char *source);
  2513.  
  2514. name is the name of the table which will be created.  source is the
  2515. complete pathname (including extension) of the ascii file to be
  2516. imported.  td is a table descriptor like the one described in Chapter
  2517. 3, and may include a primary key.  It should describe what the
  2518. resulting table will look like.  This is necessary because an ascii
  2519. file contains no details about the types or sizes of the columns it
  2520. contains. 
  2521.  
  2522. timportascii requires that each column in a row be separated by a
  2523. comma, and that each row exist on a single line of the test file.
  2524. timportascii reads data from the ascii file according to the table
  2525. descriptor you define.  The data in the ascii file must be formatted
  2526. accordingly for each type of CRDE column.  
  2527.  
  2528. CRDE
  2529. type    Expected format of column in ascii file
  2530.  
  2531. c     A character string contained in quotes "".  If the string is
  2532.     too large for the column, it will be truncated to fit.
  2533.  
  2534. i          A base 10 integer is expected. Negative values should be
  2535.     preceded with a negative sign -.  If the integer is out of
  2536.     range for the column, the value for that column is undefined.
  2537.  
  2538.  
  2539.                      - 39 -
  2540.  
  2541. l          A base 10 integer is expected.  Negative values should be
  2542.     preceded with a negative sign -.  If the integer is out of
  2543.     range for the column, the value for that column is undefined.
  2544.  
  2545. d          A date in the format of mm/dd/yyyy is expected. The date must
  2546.     be enclosed in quotes.  Whitespace is ignored.
  2547.  
  2548. f         A floating point value with any number of decimal places.
  2549.     Exponential notation may be used, +/-eNN. Negative numbers
  2550.     must be preceded with a negative sign -.  If the number is
  2551.     out of range, the resulting value imported is undefined.
  2552.  
  2553. $          Same as f.
  2554.  
  2555.  
  2556. Any whitespace between columns, and rows, is ignored by timportascii.  
  2557.  
  2558. You can export a CRDE table to an ascii file with the texportascii
  2559. function.  texportascii is declared in crde.h as
  2560.  
  2561.     long texportascii(TABLE *t, char *dest);
  2562.  
  2563. t is the table to be exported.  dest if the filename of the ascii file
  2564. to create.  Every column in the file will be separated by a comma, and
  2565. each row will appear on an individual line.  Output is formatted
  2566. depending upon datatype.  
  2567.  
  2568. CRDE
  2569. type    Format of column in ascii file 
  2570.  
  2571. c          CRDE will output a character string enclosed in quotes "".
  2572.  
  2573. i          CRDE will output a base 10 integer. Negative numbers will be
  2574.     preceded with a negative sign -.
  2575.  
  2576. l          CRDE will output a base 10 integer. Negative numbers will be
  2577.     preceded with a negative sign -.
  2578.  
  2579. d          Dates are output in the format mm/dd/yyyy and enclosed in quotes.
  2580.  
  2581. f          Floating point numbers are output with 4 decimal places.
  2582.     Negative number are preceded with a negative sign.
  2583.  
  2584. $          Dollar values are output with 2 decimal places. Negative numbers
  2585.     are preceded with a negative sign.
  2586.  
  2587.  
  2588. texportascii returns the number of rows successfully exported.
  2589.  
  2590.  
  2591.  
  2592.  
  2593.  
  2594.  
  2595.  
  2596.  
  2597.  
  2598.  
  2599.                      - 40 -
  2600.  
  2601. dBASE files
  2602.  
  2603. CRDE supports the dBASE III and dBASE III Plus file formats.  You can
  2604. import a dBASE file into a CRDE table with the timportdBASE function. 
  2605. timportdBASE is declared in crde.h as  
  2606.  
  2607.     TABLE *timportdBASE(char *name, char *source);
  2608.  
  2609. Importing a dBASE file is somewhat simpler than importing an ascii file
  2610. since no column information is required.  timportdBASE automatically
  2611. reads the dBASE file header for information about field types and
  2612. lengths and creates the CRDE table structure for you.  When converting
  2613. dBASE field types to CRDE column types, CRDE uses the smallest CRDE
  2614. column type which can correctly hold all legal values for the dBASE
  2615. field.  timportdBASE make the following translations when converting
  2616. a dBASE file to a CRDE table.  
  2617.  
  2618. dBASE
  2619. type    How dBASE field is converted to CRDE
  2620.  
  2621. C          c column equaling length of dBASE field + 1.
  2622.  
  2623. D          dBASE's dates are converted to CRDE's date format.
  2624.  
  2625. N         If the number of decimal places equals 2 then the colum type
  2626.     is $.  If decimal places is > 0 then column is type f.  If the
  2627.     length of the number field is > 11 then the column is type f.
  2628.     If the length of the number field is < 7 then the column is
  2629.     type i.  Otherwise the column is type l.
  2630.  
  2631. L          Logical fields are converted to type i. If the field is 'Y' or
  2632.     'T' the column will have a value of 1, otherwise 0.
  2633.  
  2634. M          CRDE does import memo fields.  Any memo fields in the dBASE
  2635.     file are ignored.
  2636.  
  2637.  
  2638. You can export to a dBASE file with the texportdBASE function. 
  2639. texportdBASE is declared in crde.h as  
  2640.  
  2641.     long texportdBASE(TABLE *t, char *dest);
  2642.  
  2643. texportdBASE creates the dBASE header for you by converting CRDE
  2644. columns to uppercase and creating dBASE field types which are
  2645. equivelent to each CRDE column.  texportdBASE make the following
  2646. translations when converting CRDE tables to the dBASE format.
  2647.  
  2648.  
  2649.  
  2650.  
  2651.  
  2652.  
  2653.  
  2654.  
  2655.  
  2656.  
  2657.  
  2658.  
  2659.                      - 41 -
  2660.  
  2661. CRDE
  2662. type    How CRDE column is converted to dBASE
  2663.  
  2664. c          Character field with a length equaling the length of the c
  2665.     column - 1.
  2666.  
  2667. i          Numeric field with a length of 6 and 0 decimal places.  
  2668.  
  2669. l          Numeric field with a length of 11 and 0 decimal places.  
  2670.  
  2671. d          CRDE dates are converted to dBASE format.
  2672.  
  2673. f          Numeric field with a length of 19 and 4 decimal places.  
  2674.  
  2675. $          Numeric field with a length of 19 and 2 decimal places.
  2676.  
  2677.  
  2678.  
  2679.  
  2680.  
  2681.  
  2682.  
  2683.  
  2684.  
  2685.  
  2686.  
  2687.  
  2688.  
  2689.  
  2690.  
  2691.  
  2692.  
  2693.  
  2694.  
  2695.  
  2696.  
  2697.  
  2698.  
  2699.  
  2700.  
  2701.  
  2702.  
  2703.  
  2704.  
  2705.  
  2706.  
  2707.  
  2708.  
  2709.  
  2710.  
  2711.  
  2712.  
  2713.  
  2714.  
  2715.  
  2716.  
  2717.  
  2718.  
  2719.                      - 42 -
  2720.  
  2721.  
  2722.  
  2723.  
  2724.  
  2725.  
  2726. Chapter 9
  2727.  
  2728. Working with Dates
  2729.  
  2730.  
  2731.  
  2732. The date_t type and date math
  2733.  
  2734. CRDE introduces a new date type, date_t, and 15 functions to create
  2735. and manipulate dates.
  2736.  
  2737. d columns in CRDE tables are of type date_t.  date_t is declared in
  2738. date.h as
  2739.  
  2740.     typedef long date_h;
  2741.  
  2742. CRDE represents dates by assigning a date as the number of days since
  2743. 1/1/0001.  The advantage of storing dates this way is that you don't
  2744. need to resort to special functions to add, subtract or compare dates
  2745. because you can use C's built in mathmatical abilities.  For example,
  2746. to add 7 days to a date you would simply add 7 to the date_t value
  2747.  
  2748.      d = d + 7;
  2749.  
  2750. To find the difference between 2 dates, you simply subtract them.  
  2751.  
  2752.     dif = d2 - d1;
  2753.  
  2754. You can compare dates just like any other integer types. To test if
  2755. two dates are equal, use then == operator.
  2756.  
  2757.     if (d1 == d2) { /* ... */ }
  2758.  
  2759. You can just as easily use the >, < >=, <= and != operator when
  2760. comparing dates.
  2761.  
  2762. Although storing dates as an integer makes them easy to work with in
  2763. a mathematical sense, its not very meaningful to say that you received
  2764. an order 729172 days after 0 a.d.  Thus CRDE's date functions provide
  2765. a way to easily convert dates to/from standard calender notation.
  2766.  
  2767.  
  2768. Converting calender dates
  2769.  
  2770. The mkdate function is used to convert a calender date into the date_t
  2771. type.  mkdate is declared in date.h as
  2772.  
  2773.     date_t mkdate(int month, int day, int year);
  2774.  
  2775.  
  2776.  
  2777.  
  2778.  
  2779.                      - 43 -
  2780.  
  2781. To convert the date 5/6/1968 to date_t would be  
  2782.  
  2783.     date_t d;  
  2784.  
  2785.     d = mkdate(5, 6, 1968);
  2786.  
  2787. You must remember to use the century in front of the year.  A date like 
  2788.  
  2789.     d = mkdate(5, 6, 68);
  2790.  
  2791. would return a valid date_t date, for May 5, 0068, not May 5 1968! 
  2792.  
  2793. mkdate knows about calender months, leap years, and leap centuries.
  2794. It will return -1 if the date passed to it is invalid:  like 2/29/1990.
  2795.  
  2796. Similarly, you can convert a date_t type to a calender date with the
  2797. gmdate function.  gmdate is declared in date.h as
  2798.  
  2799.     int gmdate(date_t d, int *month, int *day, int *year);
  2800.  
  2801. To extract the month, day and year from a date_t type would look
  2802. something like
  2803.  
  2804.     int month, day, year;  
  2805.  
  2806.     gmdate(d, &month, &day, &year);
  2807.  
  2808. You can extract the day, month, and year individually from a date_t
  2809. with the day, month, and year functions respectively:
  2810.  
  2811.     int day(date_t d);  
  2812.     int month(date_t d);  
  2813.     int year(date_t d);
  2814.  
  2815. Example:
  2816.  
  2817.       d = mkdate(11, 2, 1986);  
  2818.     printf("%d/%d/%d", month(d), day(d), year(d));
  2819.  
  2820. The output to the above printf function is
  2821.  
  2822.          11/2/1986
  2823.  
  2824.  
  2825. Miscellaneous date functions
  2826.  
  2827. Not all of CRDE's date functions work directly with the date_t type.
  2828. To determine if a year is a leap year, you can use the leapyear
  2829. function.  A related function, daysinyear, returns the number of days
  2830. in a given year.
  2831.  
  2832.     int leapyear(int year);
  2833.     int daysinyear(int year);
  2834.  
  2835.  
  2836.  
  2837.  
  2838.  
  2839.                      - 44 -
  2840.  
  2841. To find how many days have past since the beginning of the year, you
  2842. can use the dayofyear function.  dayofyear is declared in date.h as
  2843.  
  2844.     int dayofyear(int month, int day, int year);
  2845.  
  2846. dayofyear returns the number of days past for that date of that year. 
  2847. dayofyear has a complement function which takes the number of days and
  2848. returns the appropriate month and day.  monthday is declared as 
  2849.  
  2850.     int monthday(int days, int year, int *month, int *day);
  2851.  
  2852. Given the year and total number of days past, monthday can determine the
  2853. month and day.
  2854.  
  2855.  
  2856. Month and year math
  2857.  
  2858. Most data managers allow you to add and subtract days from dates.  But
  2859. few offer the ability to add by months or years.  Adding by months or
  2860. years is complicated by the fact that all months and years do not
  2861. contain the same number of days.  A working, but hardly adequate,
  2862. substitute used by some DBMS's is to add and subtract multiples of
  2863. 30 when adding months or multiples of 365 when adding years.  But
  2864. suppose you wanted to collect a payment one month from July 1, 1990?
  2865. Adding 30 days would result in July 31, 1990, not August 1, 1990 as
  2866. expected. 
  2867.  
  2868. CRDE provides functions for adding both months and years to a date.
  2869. They are declared in date.h as
  2870.  
  2871.     date_t monthadd(date_t d, int months);  
  2872.     date_t yearadd(date_t d, int years);
  2873.  
  2874. The monthadd function accepts a date and returns a date with months
  2875. months added to it.  monthadd correctly adds months.  Adding 1 month
  2876. to July 1, 1990 yields August 1, 1990.  You can subtract months from
  2877. a date by simply making the months parameter negative.  yearadd works
  2878. in a similar fashion.  yearadd returns a date +/- the number of years
  2879. specified.  
  2880.  
  2881.  
  2882. Day of week functions
  2883.  
  2884. Often times when using dates, the day of week can be significant.
  2885. For example, you might want to only allow orders to be shipped during
  2886. the week, and not on the weekend.  CRDE provides two useful functions
  2887. which use the day of week.  dayofweek returns the day of week of the
  2888. date specified.  dayofweek is declared in date.h as
  2889.  
  2890.     int dayofweek(date_t);
  2891.  
  2892. dayofweek returns a constant between 0 and 6 which stand for the days
  2893. of the week between Sunday and Saturday.  To help the translation, has
  2894. constants declared in date.h for every day of the week.
  2895.  
  2896.  
  2897.  
  2898.  
  2899.                      - 45 -
  2900.  
  2901.     #define SUNDAY        0
  2902.     #define MONDAY        1
  2903.     #define TUESDAY     2
  2904.     #define WEDNESDAY    3
  2905.     #define THURSDAY    4
  2906.     #define FRIDAY          5
  2907.     #define SATURDAY    6
  2908.  
  2909. firstday is a useful function to determine the date of the first
  2910. Monday, Tuesday, Wednesday, etc. of the month.  firstday  is declared
  2911. in date.h as  
  2912.  
  2913.     date_t firstday(int month, int year, int dayofweek);
  2914.  
  2915. Suppose you wanted an order to be placed on the first Monday of
  2916. January,1990.  You could easily accomplish this with firstday
  2917.  
  2918.     d = firstday(1, 1990, MONDAY);
  2919.  
  2920. firstday returns the date as a date_t type.  MON is a constant for a
  2921. day of the week as described above.  firstday is useful in many
  2922. applications.  You can also use firstday for calculating the second,
  2923. third, and fourth Monday, Tuesday, etc. of a month by simply
  2924. calculating the first day and adding the appropriate number of weeks.
  2925. For example, the following function returns the date of the fourth
  2926. thursday of any month of any year:  
  2927.  
  2928.     date_t FourthThu(int month, int year)  {    
  2929.       int i;
  2930.  
  2931.       i = firstday(month, year, THURSDAY);
  2932.       return i >= 0 ? i + 21 : i;
  2933.     }
  2934.  
  2935.  
  2936.  
  2937.  
  2938.  
  2939.  
  2940.  
  2941.  
  2942.  
  2943.  
  2944.  
  2945.  
  2946.  
  2947.  
  2948.  
  2949.  
  2950.  
  2951.  
  2952.  
  2953.  
  2954.  
  2955.  
  2956.  
  2957.  
  2958.  
  2959.                      - 46 -
  2960.  
  2961.  
  2962.  
  2963.  
  2964.  
  2965.  
  2966. Chapter 10
  2967.  
  2968. Temporary Tables
  2969.  
  2970.  
  2971.  
  2972. STATIC tables
  2973.  
  2974. The concept of a temporary table is very important in CRDE.  Unlike
  2975. in other RDBMS's temporary tables have special meaning in CRDE.
  2976. Temporary tables can be useful in a number of situations,
  2977. especially in queries.
  2978.  
  2979. In CRDE you can specifically designate a table as temporary.  There
  2980. are actually two types of temporary tables in CRDE, the first of
  2981. which is STATIC.  To specify a table as a temporary table, simply
  2982. use STATIC instead of a regular table name like "sups".  For example:
  2983.  
  2984.     answer = tcreat(STATIC, "*s# i, sname c26, city c26, status i");
  2985.  
  2986. STATIC is defined in crde.h.  You can use STATIC wherever you would
  2987. use a normal table name.
  2988.  
  2989.     t = tcreat(STATIC, "*s# i, sname c26, city c26, status i"); 
  2990.     t = tselect(STATIC, sups, "city", EQ, "Boston", NULL);
  2991.  
  2992. Using STATIC relieves the programmer from having to give temporary
  2993. tables some unique name.  CRDE automatically generates a unqiue name
  2994. for the table which is not seen by the user.  Temporary tables can be
  2995. distinguished by TABLE * each is assigned to.  You may open any number
  2996. temporary tables, as long as the total number of tables does not
  2997. exceed 128.
  2998.  
  2999.  
  3000. Virtual tables
  3001.  
  3002. Another advantage of CRDE's temporary tables is that CRDE can create
  3003. and maintain them completely in memory.  This has several advantages,
  3004. the greatest of which is speed.  The overhead of creating a file(s)
  3005. for the table is removed, which also means that no file handles are
  3006. used by the temporary table.  All reads and writes to the table go
  3007. directly to memory so any operations performed on it are extremely
  3008. fast.  Sometimes a temporary table may be too big to fit in memory, or
  3009. it might be hogging so much memory that CRDE cannot perform its other
  3010. operations efficiently. When this happens, CRDE will place part or all
  3011. of the table on disk to release memory.   This technique is called
  3012. transparent migration.  CRDE will assign the table a unique table name
  3013. and flush it to disk.  The important thing is that CRDE handles all the
  3014. details in managing temporary tables for you.  You never need to worry
  3015. about whether a temporary table is on disk, in memory, or both.  You
  3016. don't even have to worry cleaning up afterwards.  When you close the
  3017. temporary table, the image on disk (if any) is removed automatically.
  3018.  
  3019.                      - 47 -
  3020.  
  3021. Temporary files created by CRDE will have the format "~TMPXXXX " where
  3022. XXXX is a hexadecimal number ranging from 0x0 to 0xFFFF.  Normally,
  3023. these files are deleted automatically for you by CRDE.  Sometimes,
  3024. however, you may find one or more of the lying around on your disk.
  3025. This can happen is you forget to close a temporary table or your
  3026. program terminates abnormally.  You can erase these files since they
  3027. are no longer used by CRDE. 
  3028.  
  3029. Despite all these special abilities, CRDE places no restrictions on
  3030. what you can do with temporary tables.  You can use a temporary table
  3031. any place you would use a regular one. You can query them, update them,
  3032. create indexes on them, and even perform transactions on them.
  3033.  
  3034.  
  3035. TEMP tables and automatic cleanup
  3036.  
  3037. The second type of temporary table is the TEMP table.  TEMP tables are
  3038. identical to STATIC tables in every way except one.  TEMP tables support
  3039. a feature called auto-cleanup, which means if you use it correctly, you
  3040. don't even need to specifically close the table when your through with
  3041. it.  CRDE will know when you are through with it and will close (and
  3042. delete) it for you automatically.
  3043.  
  3044. When and how would you want to use this feature?  Well maybe an example
  3045. would better illustrate the point.  Suppose you use tselect to select
  3046. all the rows from the sups table where city EQ "Boston"?  You also
  3047. want it sorted by the supplier's name.
  3048.  
  3049.     t = tselect("temp1", sups, "city", EQ, "Boston", NULL);  
  3050.     r = tsort("temp2", t, "sname");  
  3051.     tdrop(t);
  3052.  
  3053. Notice how this requires three steps.  Notice how you must remember
  3054. to drop t.  Otherwise it would just lie around wasting memory. Now
  3055. lets try declaring t as a TEMP table.
  3056.  
  3057.     t = tselect(TEMP, sups, "city", EQ, "Boston", NULL);  
  3058.     r = tsort("temp", t, "sname");
  3059.  
  3060. Notice the tdrop(t) statement is gone.  Because t is TEMP, it was
  3061. automatically dropped by tsort for you.
  3062.  
  3063. But here's where the real advantage of TEMP tables shines.  We can
  3064. save another step by nesting the tselect statement within tsort!
  3065.  
  3066.     r = tsort(STATIC,
  3067.         tselect(TEMP, sups, "city", EQ, "Boston", NULL),
  3068.         "sname"
  3069.     );
  3070.  
  3071. There is no limit to the number of TEMP tables which may be nested
  3072. in this manner.
  3073.  
  3074.  
  3075.  
  3076.  
  3077.  
  3078.  
  3079.                      - 48 -
  3080.  
  3081. What happens if you nest a table which is not a TEMP table?  Well
  3082. since its not a TEMP table it won't be automatically dropped by the
  3083. function which receives it.  And because you have no access to it
  3084. (its not assigned to any variable), it will simply lie around for
  3085. the duration of your program, using up memory.  Eventually, CRDE will
  3086. realize that the table is not being used and will release as much
  3087. memory from the table as it possibly can. However, there will always
  3088. be a small piece of the table (usually about 1k) which will remain in
  3089. memory until the program terminates.
  3090.  
  3091.  
  3092. Rules for using TEMP tables
  3093.  
  3094. Which functions support auto-cleanup of TEMP tables?  Practically all
  3095. of them.  You can determine if a table will be automatically dropped
  3096. by a CRDE function by following this simple rule:
  3097.  
  3098.     A CRDE function will automatically drop a TEMP table passed
  3099.     to it as a parameter if the function does not or can not
  3100.     alter the table in any way.  If the TEMP table cannot be thus
  3101.     affected by the function, it will be automatically dropped when
  3102.     the function completes, even in the event of an error.
  3103.  
  3104. Altering a table means inserting, changing, or deleting rows or columns
  3105. in the table.  Here is a list of all CRDE functions which can alter
  3106. tables:        
  3107.  
  3108.     tadd        tchange        tchangeif      tchcol    
  3109.     tdelete        tdeleteif        tdropindex     tempty 
  3110.         tindex         tinsert        tload          treplace 
  3111.        tsubtract      tupdate
  3112.  
  3113. For every rule, there are always exceptions.  Some CRDE functions will
  3114. not automatically drop a TEMP table, regardless of whether they alter
  3115. it.  These functions are generally those ones related to transaction
  3116. tracking and memory management.
  3117.  
  3118.     tcommit        tflush         tnormal        tmark     
  3119.      tmode          trelease        trollback      ttransact 
  3120.      twritec
  3121.  
  3122. If a CRDE function accepts two tables as parameters, it will drop
  3123. either of them if they are TEMP.  However, a TEMP table in read-only
  3124. mode will not be dropped automatically by any CRDE function.  This can
  3125. only occur if the TEMP table is used in one of CRDE's "..if" functions.
  3126. The table will not be automatically dropped by any CRDE function calls
  3127. in the user-defined function specified in the "..if" function.
  3128.  
  3129. You will find using both TEMP and STATIC tables invaluable in your
  3130. applications.  Use TEMP whenever you nest a CRDE function which returns
  3131. a table, inside another CRDE function, otherwise use STATIC.
  3132.  
  3133.  
  3134.  
  3135.  
  3136.  
  3137.  
  3138.  
  3139.                      - 49 -
  3140.  
  3141.  
  3142.  
  3143.  
  3144.  
  3145.  
  3146. Chapter 11
  3147.  
  3148. Performing Queries
  3149.  
  3150.  
  3151.  
  3152. What is a query?
  3153.  
  3154. A query is a question that you ask about the information in your
  3155. database.  You might want to find out which suppliers are supplying
  3156. a certain part, or which how many red parts are currently on order,
  3157. or how many suppliers supply both bolts and nuts, etc.  Queries have
  3158. many uses in applications. Queries are usually used to answer questions,
  3159. create lookup or view tables, make calculations, or generate reports.
  3160.  
  3161. CRDE contains very powerful querying capabilities, matching those found
  3162. in high level querying languages such as SQL.  Because it is rooted in
  3163. a procedural language however, CRDE has abilities which range far
  3164. beyond the capabilities of other relational languages.  CRDE provides
  3165. the tools to create queries as complex as you could possibly imagine.
  3166.  
  3167. Developing queries in any relational language is an art form of sorts,
  3168. and doing so in CRDE is no exception.  This chapter attempts to look
  3169. at many different types of queries, and CRDE's approach to each.  To
  3170. assist you, each query is first described in English.  The CRDE answer
  3171. of the query will appear next followed by an equivalent SQL query.
  3172. This will help you more quickly understand the nuances of how to create
  3173. CRDE queries.  The answer to each query is also shown.
  3174.  
  3175. Each of the following queries are just small portions of code and not
  3176. meant to be entire programs.  All assume that the following declarations
  3177. have been made:
  3178.  
  3179.     TABLE *sups, *parts, *orders, *answer, *set;  
  3180.  
  3181.     typedef struct {    
  3182.         int s;    
  3183.         char sname[26];    
  3184.         char city[16];    
  3185.         int status;   
  3186.     } sup;  
  3187.  
  3188.     typedef struct {    
  3189.         int p;    
  3190.         char pname[16];    
  3191.         char color[11];  
  3192.     } part;  
  3193.  
  3194.  
  3195.  
  3196.  
  3197.  
  3198.  
  3199.                      - 50 -
  3200.  
  3201.     typedef struct {    
  3202.         int s;    
  3203.         int p;    
  3204.         int qty;  
  3205.     } order;  
  3206.  
  3207.     sups = tcreat(STATIC, "*s# i, sname c26, city, c16, status i");  
  3208.  
  3209.     tload(sups, 1, "Smith, John", "Boston", 5);  
  3210.     tload(sups, 2, "Jones, Ben", "New York", 10);  
  3211.     tload(sups, 3, "Barnes, Frank", "Boston", 15);  
  3212.     tload(sups, 4, "Landon, Mark", "Auburn Hills", 20);  
  3213.     tload(sups, 5, "Andrews, Jim", "New York", 5);  
  3214.  
  3215.     parts = tcreat(STATIC, "*p# i, pname c16, color c11");  
  3216.     tload(parts, 1, "bolt", "red");  
  3217.     tload(parts, 2, "screw", "red");  
  3218.     tload(parts, 3, "screw", "blue");  
  3219.     tload(parts, 4, "nut", "gray");  
  3220.     tload(parts, 5, "bolt", "gray");  
  3221.     tload(parts, 6, "nail", "red");  
  3222.  
  3223.     orders = tcreat(STATIC, "*s# i, *p# i, qty i");  
  3224.     tload(orders, 1, 1, 100);  
  3225.     tload(orders, 1, 2, 200);  
  3226.     tload(orders, 1, 3, 300);  
  3227.     tload(orders, 1, 4, 400);  
  3228.     tload(orders, 1, 5, 500);  
  3229.     tload(orders, 1, 6, 600);  
  3230.     tload(orders, 2, 1, 100);  
  3231.     tload(orders, 2, 2, 100);  
  3232.     tload(orders, 2, 6, 100);  
  3233.     tload(orders, 3, 1, 200);  
  3234.     tload(orders, 3, 2, 50);  
  3235.     tload(orders, 4, 3, 100);  
  3236.     tload(orders, 4, 5, 500);
  3237.  
  3238. /*
  3239. sups
  3240.  
  3241.     s#     sname        city        status
  3242.     -------    ---------------    ---------------    -------
  3243.     1    Smith, John    Boston        5
  3244.     2    Jones, Ben    New York    10
  3245.     3    Barnes, Frank    Boston        15
  3246.     4    Landon, Mark    Auburn        20
  3247.     5    Andrews, Jim    New York    5
  3248.  
  3249.  
  3250.  
  3251.  
  3252.  
  3253.  
  3254.  
  3255.  
  3256.  
  3257.  
  3258.  
  3259.                      - 51 -
  3260.  
  3261. parts
  3262.  
  3263.     p#    pname        color
  3264.     -------    ---------------    -----------
  3265.     1    bolt        red
  3266.     2    screw        red
  3267.     3    screw        blue
  3268.     4    nut        gray
  3269.     5    bolt        gray
  3270.     6    nail        red
  3271.  
  3272. orders
  3273.  
  3274.     s#    p#    qty
  3275.     -------    -------    -------
  3276.     1    1    100
  3277.     1    2    200
  3278.     1    3    300
  3279.     1    4    400
  3280.     1    5    500
  3281.     1    6    600
  3282.     2    1    100
  3283.     2    2    100
  3284.     2    6    100
  3285.     3    1    200
  3286.     3    2    50
  3287.     4    3    100
  3288.     4    5    500
  3289. */
  3290.  
  3291.  
  3292.  
  3293. Simple Queries
  3294.  
  3295. Simple Select Queries
  3296.  
  3297. The majority of queries will involve selecting rows from a single
  3298. table based on one or more conditions.  These types of queries can
  3299. be answered with the tselect function.  tselect is very much like
  3300. using an SQL SELECT statement with the * operator.  tselect, however,
  3301. is limited to querying only a single table at a time.  Suppose you
  3302. wanted find all the suppliers who live in Boston.
  3303.  
  3304.     answer = tselect(STATIC, sups, "city", EQ, "Boston", NULL);  
  3305.  
  3306.     SELECT * FROM SUPS WHERE CITY = "Boston"
  3307.  
  3308.  
  3309.     s#    sname        city        status
  3310.     -------    ---------------    ---------------    -------
  3311.     1    Smith, John    Boston        5
  3312.     3    Barnes, Frank    Boston        15
  3313.  
  3314.  
  3315.  
  3316.  
  3317.  
  3318.  
  3319.                      - 52 -
  3320.  
  3321. Note:  unless you want to keep the results of a query permanent,
  3322. which is rare, you will probably want all of your queries to be of
  3323. type STATIC.  Queries nested inside of other queries should always
  3324. be of type TEMP.  
  3325.  
  3326. CRDE can also create compound expressions by adding more conditions
  3327. to the search expression.  You may only want those suppliers who live
  3328. in Boston and have a status greater than 5.
  3329.  
  3330.     answer = tselect(STATIC, sups, "city", EQ, "Boston",
  3331.       "status", GT, 5, NULL);
  3332.  
  3333.     SELECT * FROM SUPS WHERE CITY = "Boston" AND STATUS > 5
  3334.  
  3335.  
  3336.     s#    sname        city        status
  3337.     -------    ---------------    ---------------    -------
  3338.     3    Barnes, Frank    Boston        15
  3339.  
  3340.  
  3341. ORing queries
  3342.  
  3343. Notice that CRDE can handle queries in which the conditions are
  3344. logically anded together in a fairly straightforward manner.  But
  3345. suppose you wanted a list of all suppliers who live in Boston or
  3346. have a status > 5? There are several ways to handle this.  The
  3347. simplest is to break up the query into 2 separate queries and union
  3348. them together with the tunion function.  
  3349.  
  3350.     answer = tunion(STATIC,
  3351.       tselect(TEMP, sups, "city", EQ, "Boston", NULL),
  3352.       tselect(TEMP, sups, "status", GT, 5, NULL)
  3353.     );
  3354.  
  3355.     SELECT * FROM SUPS WHERE CITY = "Boston" OR STATUS > 5
  3356.  
  3357.     
  3358.     s#    sname        city        status
  3359.     -------    ---------------    ---------------    -------
  3360.     1    Smith, John    Boston        5
  3361.     2    Jones, Ben    New York    10
  3362.     3    Barnes, Frank    Boston        15
  3363.     4    Landon, Mark    Auburn        20
  3364.  
  3365.  
  3366. tunion is declared in crde.h as
  3367.  
  3368.     TABLE *tunion(char *name, TABLE *t1, TABLE *t2);
  3369.  
  3370. tunion returns a table containing all the rows found in t1 and t2.
  3371. t1 and t2 must be compatible, i.e. they must be identical on a column
  3372. by column basis in both type and size.  They may have different
  3373. primary and/or secondary keys however.  For example, the following
  3374. tables would all be compatible with one another
  3375.  
  3376.  
  3377.  
  3378.  
  3379.                      - 53 -
  3380.  
  3381.     tcreat(STATIC, "s# i, sname c26, city 26, status i");
  3382.     tcreat(STATIC, "*s# i, sname c26, city c26, status i");
  3383.     tcreat(STATIC, "sup# i, name c26, loc c26, stat i");
  3384.  
  3385. while these are not
  3386.  
  3387.     tcreat(STATIC, "s# i, sname c26, city, c26, status i");
  3388.     tcreat(STATIC, "s# i, sname c26, city c26");
  3389.     tcreat(STATIC,  "s# i, sname c16, city c26, status i");
  3390.     tcreat(STATIC,  "s# l, sname c26, city c26, status i");
  3391.  
  3392. The structure of the resulting table will be identical to t1, and will
  3393. have the same primary key, if any.  This is important to remember
  3394. because tunion will may return two different tables depending upon
  3395. whether t1 is keyed.   
  3396.  
  3397. In this particular query, tunion is only effective if sups has a
  3398. primary key, which in this case it does.  If sups is not keyed, a
  3399. problem can arise if there exist rows in the table where a supplier
  3400. lives in Boston and has a status > 10.  If this is true, then both
  3401. the innermost tselect statements will collect the same row.  The
  3402. difference is that when sups is keyed, the resulting union, which is
  3403. also keyed, filters out any duplicate rows automatically.  If sups were
  3404. not keyed, then any rows in which the supplier lives in Boston and has
  3405. a status > 10 would appear twice in the answer table.
  3406.  
  3407. This is just one in many cases where the existence, or absence, of a
  3408. primary key can affect the result of a query.  You need to keep this
  3409. in mind when designing your tables and the queries you will need to
  3410. perform on them. 
  3411.  
  3412. Another way to perform the query would be to use tselectif and write
  3413. a simple "..if" function to perform the query.
  3414.  
  3415.     int iffunc(void *rec)  {    
  3416.       return strcmp(((sup *)rec)->city, "Boston") == 0
  3417.         ||  ((sup *)rec)->status > 5;
  3418.     }
  3419.  
  3420.     answer = tselectif(STATIC, sups, iffunc, NULL);
  3421.  
  3422. Either is an acceptable answer to the query.
  3423.  
  3424.  
  3425. Projecting columns
  3426.  
  3427. CRDE gives you the ability to project columns from a table with its
  3428. tproject function.  tproject is much like tselect except it specifies
  3429. only those columns which it wants to display.  For example, if you
  3430. wanted a list of all supplier names and the city they live in.
  3431.  
  3432.     answer = tproject(STATIC, sups, "sname,city", NULL);  
  3433.  
  3434.     SELECT SNAME, CITY FROM SUPS
  3435.  
  3436.  
  3437.  
  3438.  
  3439.                      - 54 -
  3440.  
  3441.     sname        city
  3442.     ---------------    ----------------
  3443.     Smith, John    Boston
  3444.     Jones, Ben    New York
  3445.     Barnes, Frank    Boston
  3446.     Landon, Mark    Auburn
  3447.     Andrews, Jim    New York
  3448.  
  3449.  
  3450. In CRDE however, it is generally more common to select rows rather than
  3451. to project them.  Usually you won't bother to remove those columns
  3452. which you don't really need in queries except in special occasions.
  3453. However, there is one type of query where tproject shines.  That is
  3454. when selecting distinct values from a column.
  3455.  
  3456.  
  3457. Selecting distinct values
  3458.  
  3459. tproject is primarily used to find distinct values in a column.
  3460. tproject expects a special type of column list which allows you to set
  3461. the primary key of the resulting table by prepending column names with
  3462. an asterisk '*', similar to a table descriptor.  The result of this is
  3463. that all duplicate values in the column(s) marked as part of the
  3464. primary key will be filtered out automatically by CRDE when the new
  3465. table in generated, since no two rows in a table may have the same key
  3466. values.  
  3467.  
  3468. Suppose you wanted a list of all supplier #'s who have made orders.
  3469. Several supplier #'s exist more than once in the orders table.
  3470. However, by defining the answer table to be "*s#" any duplicates
  3471. are filtered out automatically.  
  3472.  
  3473.     answer = tproject(STATIC, orders, "*s#", NULL);  
  3474.  
  3475.     SELECT DISTINCT S# FROM ORDERS
  3476.  
  3477.  
  3478.     s#
  3479.     -------
  3480.     1
  3481.     2
  3482.     3
  3483.     4
  3484.  
  3485.  
  3486. Joining tables
  3487.  
  3488. The heart of any relational language is the ability to relate
  3489. information between tables.  In relational terms, this is called
  3490. "joining" tables. You can join tables with CRDE's powerful tjoin
  3491. function.  The orders table consists of three columns s#, p#, and qty.
  3492. Suppose we wanted a list of all orders which included all the details
  3493. about the supplier making the order. All the information about
  3494. suppliers exists in the sups table.  By using tjoin, you can merge the
  3495. information in sups and orders together into a single table.  
  3496.  
  3497.  
  3498.  
  3499.                      - 55 -
  3500.  
  3501.     answer = tjoin(STATIC, sups, "s#", orders, "s#",
  3502.       "s#,sname,city,status,p#,qty");
  3503.  
  3504.     SELECT S#,SNAME,CITY,STATUS,P#,QTY FROM SUPS,ORDERS WHERE
  3505.       SUPS.S# = ORDERS.S#
  3506.  
  3507.  
  3508.     s#     sname        city        status    p#    qty
  3509.     -------    ---------------    ---------------    -------    -------    -------
  3510.     1    Smith, John    Boston        5    1    100
  3511.     1    Smith, John    Boston        5    2    200
  3512.     1    Smith, John    Boston        5    3    300
  3513.     1    Smith, John    Boston        5    4    400
  3514.     1    Smith, John    Boston        5    5    500
  3515.     1    Smith, John    Boston        5    6    600
  3516.     2    Jones, Ben    New York    10    1    100
  3517.     2    Jones, Ben    New York    10    2    100
  3518.     2    Jones, Ben    New York    10    6    100
  3519.     3    Barnes, Frank    Boston        15    1    200
  3520.     3    Barnes, Frank    Boston        15    2    50
  3521.     4    Landon, Mark    Auburn        20    3    100
  3522.     4    Landon, Mark    Auburn        20    5    500
  3523.     
  3524.  
  3525. tjoin is declared in crde.h as 
  3526.  
  3527.     TABLE *tjoin(char *name, char *cl1, TABLE *t1, char *cl2, TABLE
  3528.       *t2, char *pl);
  3529.  
  3530. tjoin joins t1 and t2 to create a new table.  cl1 and cl2 are column
  3531. lists describing the join columns for each table.  Up to 256 columns
  3532. may be specified.  tjoin joins those rows from either table which have
  3533. identical values in their corresponding  join columns.  Join columns
  3534. must be identical in both type and size on a column by column basis:
  3535. c columns must be joined to c columns, i columns  must be joined to i
  3536. columns, etc.   
  3537.  
  3538. pl lists those columns to be included in the result table.  pl may
  3539. contain from both t1 and t2.  A problem might occur however, if there
  3540. exists a column in both t1 adn t2 with the same name.  You could not
  3541. include both in the result table because CRDE does not allow tables
  3542. with duplicate column names.  If you specify one of the columns, CRDE
  3543. will pick the one from t1 by default.  You should take this into
  3544. consideration when creating tables you may plan to join later.
  3545.  
  3546. Like tproject, tjoin's column list may define a primary key.  You can
  3547. use this to remove duplicate values from the result table.  For
  3548. example, suppose you wanted a list of supplier's names who have
  3549. placed orders.
  3550.  
  3551.     answer  = tjoin(STATIC, sups, "s#", orders, "s#", "*s#,sname");
  3552.  
  3553.     SELECT DISTINCT S#, SNAME FROM SUPS, ORDERS
  3554.  
  3555.  
  3556.  
  3557.  
  3558.  
  3559.                      - 56 -
  3560.  
  3561.     s#    sname
  3562.     -------    ----------------
  3563.     1    Smith, John
  3564.     2    Jones, Ben
  3565.     3    Barnes, Frank
  3566.     4    Landon, Mark
  3567.  
  3568.  
  3569. Order by queries 
  3570.  
  3571. Often you may want to sort a query in a particular order for viewing
  3572. or reporting.  Whenever you perform a query in CRDE, using tselect,
  3573. tjoin, or any other function which returns a table as a result, the
  3574. order of the rows in the answer table is undefined.  If you want to
  3575. put them in order you must use the tsort function to sort the answer
  3576. table.  Remember the first query where we wanted all the suppliers
  3577. who lived in Boston.  Suppose we want that list in alphabetic order
  3578. by the supplier's name.  You can do this by simply nesting the query
  3579. inside a tsort function call.
  3580.  
  3581.     answer = tsort(STATIC,
  3582.       tselect(TEMP, sups, "status", GT, 5, NULL),
  3583.       "sname"
  3584.     );
  3585.  
  3586.     SELECT * FROM SUPS WHERE STATUS > 5 ORDER BY SNAME
  3587.  
  3588.  
  3589.     s#    sname        city        status
  3590.     -------    ---------------    ---------------    -------
  3591.     3    Barnes, Frank    Boston        15
  3592.     2    Jones, Ben    New York    10
  3593.     4    Landon, Mark    Auburn        20
  3594.  
  3595.     
  3596. tsort is declared in crde.h as
  3597.  
  3598.     TABLE *tsort(char *name, TABLE *t, char *id);
  3599.  
  3600. tsort returns a sorted copy of t according to the index descriptor in
  3601. id.  You can sort a column in descending order by prepending its name
  3602. with an exclaimation point '!'.  For example, to sort the above query
  3603. in descending order would look like
  3604.  
  3605.     answer = tsort(STATIC,
  3606.       tselect(TEMP, sups, "city", EQ, "Boston", NULL),
  3607.       "!sname"
  3608.     );
  3609.  
  3610.     SELECT * FROM SUPS WHERE STATUS > 5 ORDER BY SNAME DESC
  3611.  
  3612.  
  3613.  
  3614.  
  3615.  
  3616.  
  3617.  
  3618.  
  3619.                      - 57 -
  3620.  
  3621.     s#    sname        city        status
  3622.     -------    ---------------    ---------------    -------
  3623.     4    Landon, Mark    Auburn        20
  3624.     2    Jones, Ben    New York    10
  3625.     3    Barnes, Frank    Boston        15
  3626.     
  3627.  
  3628. tsort should always be the outermost functions call in a query.  
  3629.  
  3630.  
  3631. Statistical queries, making calculations  
  3632.  
  3633. CRDE has special functions to calculate the count, min, max, average,
  3634. and sum of a column.  These are tcount, tmin, tmax, taverage, and tsum
  3635. respectively.  Unlike in SQL, CRDE stores the result of these queries
  3636. in a variable, not a table.
  3637.  
  3638. All of CRDE's statistcal functions have the same declaration:
  3639.  
  3640.     int statfunc(TABLE *t, char *c, void *result);
  3641.  
  3642. where statfunc is tcount, tmin, tmax, taverage, or tsum.  t is the table
  3643. being queried.  c is the column to perform the statiscal analysis on.
  3644. result is a pointer to a buffer which will hold the result of the query.
  3645. The result type is dependent upon the function you are using and the
  3646. datatype of the column being queried.  statfunc returns 0 if the query
  3647. was successful.  
  3648.  
  3649. Find the maximum status of any supplier.  
  3650.  
  3651.     int i;  
  3652.  
  3653.     tmax(sups, "status", &i);  
  3654.  
  3655.     SELECT MAX(STATUS) FROM SUPS
  3656.  
  3657.  
  3658.     max(status)
  3659.  
  3660.     20
  3661.  
  3662.  
  3663. CRDE places the result of tmax into i, which must be the same type as
  3664. the column being queried, int.  Not all of the statistical query
  3665. functions expect a result type to be the same as the column's type
  3666. however. For example, tsum and taverage always expect the result to be
  3667. of type double, regardless of the column type.
  3668.  
  3669.     double d;  
  3670.  
  3671.     tsum(orders, "qty", &d);  
  3672.  
  3673.     SELECT SUM(QTY) FROM ORDERS
  3674.  
  3675.  
  3676.  
  3677.  
  3678.  
  3679.                      - 58 -
  3680.  
  3681.     sum(qty)
  3682.  
  3683.     3250
  3684.  
  3685.  
  3686. You can take statistical queries of a given subset of a table by simply
  3687. nesting a query within the statistical function.  For example, to find
  3688. the maximum status of any supplier who lives in Boston
  3689.  
  3690.     int i;  
  3691.  
  3692.     tmax(tselect(TEMP, sups, "city", EQ, "Boston", NULL),
  3693.       "status", &i);
  3694.  
  3695.     SELECT MAX(STATUS) FROM SUPS WHERE CITY = "Boston"
  3696.  
  3697.  
  3698.     max(status)
  3699.  
  3700.     15
  3701.  
  3702.  
  3703. You might be wondering what tmax would return if the table it is
  3704. querying is empty.  You can't find the maximum of nothing.  In a truly
  3705. relational language, the result of such a query would be NULL, or
  3706. unknown.  However, since CRDE does not support NULL values, it returns
  3707. an error code instead, specifying that it could not answer the query.
  3708.  
  3709.  
  3710. Set-related queries
  3711.  
  3712. CRDE provides two very powerful set-comparison functions: tdiff and
  3713. tintersect.  They are declared in crde.h as
  3714.  
  3715.     TABLE *tdiff(char *name,  TABLE *t1, TABLE *t2);
  3716.     TABLE *tintersect(char *name, TABLE *t1, TABLE *t2);
  3717.  
  3718. tdiff creates the relational difference between t1 and t2.  The
  3719. resulting table will contain all those rows in t1 which are not found
  3720. in t2.  tintersect is just the opposite.  It returns a table containing
  3721. all the rows in t1 which are also found in t2.  Like tunion,  t1 and t2
  3722. must have compatible structures, i.e. they must be identical in both
  3723. type and size on a column by column basis.   The resulting table will
  3724. have a structure identical to t1.  
  3725.  
  3726. This query uses tdiff to determine which suppliers have not made any
  3727. orders.
  3728.  
  3729.     answer = tdiff(STATIC,
  3730.       tproject(TEMP, sups, "s#", NULL),
  3731.       tproject(TEMP, orders, "*s#", NULL)
  3732.     );
  3733.  
  3734.     s#
  3735.     -------
  3736.     5
  3737.  
  3738.  
  3739.                      - 59 -
  3740.  
  3741. Notice inside tdiff two sets are created: one containing every supplier #,
  3742. and one containing every supplier # who made an order.  The resulting
  3743. difference leaves those supplier's who have not made any orders.  Note
  3744. that the tables are compatible:  both contain a single column of type i.
  3745. Although one table is keyed and one is not, tdiff does not complain.
  3746. Two tables do not have to have the same primary key to be compatible.
  3747.  
  3748. This query uses tintersect to find those parts ordered by both supplier
  3749. #1 and supplier #2.
  3750.  
  3751.     answer = tintersect(STATIC,
  3752.       tproject(TEMP, orders, "p#", "s#", EQ, 1, NULL),
  3753.       tproject(TEMP, orders, "p#", "s#", EQ, 2, NULL)
  3754.     );
  3755.  
  3756.  
  3757. Complex Queries
  3758.  
  3759. Subqueries
  3760.  
  3761. Suppose you wanted a list of all suppliers who have a status greater
  3762. than the average status.
  3763.  
  3764.     double d;  
  3765.  
  3766.     taverage(sups, "status", &d);  
  3767.     answer = tselect(STATIC, sups, "status", GT, (int)d, NULL);  
  3768.  
  3769.     SELECT * FROM SUPS WHERE STATUS > (SELECT AVG(STATUS) FROM S)
  3770.  
  3771.  
  3772.     s#    sname        city        status
  3773.     -------    ---------------    ---------------    -------
  3774.     3    Barnes, Frank    Boston        15
  3775.     4    Landon, Mark    Auburn        20
  3776.  
  3777.  
  3778. Notice how d must be typecast (int) in the tselect statement because
  3779. the result of taverage is always double.
  3780.  
  3781.  
  3782. Correlated subqueries
  3783.  
  3784. A correlated subquery is a special type of subquery.  In a correleated
  3785. subquery, the calculated subquery is dependent upon the current row
  3786. being queried.  In SQL, this type of query requires that you use an
  3787. alias.  In CRDE it requires and "..if" function.
  3788.  
  3789. This query returns all suppliers which have a status greater than
  3790. the average status for that supplier's particular city.
  3791.  
  3792.  
  3793.  
  3794.  
  3795.  
  3796.  
  3797.  
  3798.  
  3799.                      - 60 -
  3800.  
  3801.     int iffunc(void *rec)  {    
  3802.         double d;    
  3803.         int i;    
  3804.  
  3805.         i = taverage(
  3806.           tselect(TEMP, sup, "city", EQ, ((sup *)rec)->city, NULL),
  3807.           "status", &d
  3808.         );    
  3809.         return i ? i : ((sup *)rec)->status >= d;  
  3810.     }  
  3811.  
  3812.     answer = tselectif(STATIC, sups, iffunc, NULL);  
  3813.  
  3814.     SELECT * FROM SUPS X WHERE STATUS >= (SELECT AVG(STATUS)
  3815.       FROM S WHERE CITY = X.CITY)
  3816.  
  3817.  
  3818.     s#    sname        city        status
  3819.     -------    ---------------    ---------------    -------
  3820.     2    Jones, Ben    New York    10
  3821.     3    Barnes, Frank    Boston        15
  3822.     4    Landon, Mark    Auburn        20
  3823.  
  3824.  
  3825. CRDE uses the tselectif function to perform this query.  Every row in
  3826. the table is passed to iffunc for evaluation.  iffunc calculates the
  3827. average status of all suppliers for that row's city, then returns true
  3828. if the status for the row is greater than that average.
  3829.  
  3830.  
  3831. Multi-table joins
  3832.  
  3833. In relational databases multi-table (3 or more) joins are commonplace.
  3834. Although CRDE does not directly support multi-table joins, you can
  3835. simulate them using multiple tjoin calls.  The technique for a three
  3836. table join is to join the first two tables together, and then join the
  3837. third table to the result.  To perform a four table join, simply
  3838. perform a three table join, and then join the fourth table to the
  3839. result, and so on.  There is no limit to the number tables which can
  3840. be joined in this fashion.
  3841.  
  3842. The following demonstrates the three table join by joining the sups,
  3843. parts, and orders table into a single table.  The query is split into
  3844. two parts (instead of being nested) for clarity.
  3845.  
  3846.     temp = tjoin(TEMP, parts, "p#", orders, "p#", "s#,p#,pname,qty");
  3847.  
  3848.     answer = tjoin(STATIC, sups, "s#", temp, "s#",
  3849.       "s#,sname,p#,pname,qty"
  3850.     );
  3851.  
  3852.     SELECT * FROM SUPS,PARTS,ORDERS WHERE SUPS.S# = ORDERS.S#
  3853.       AND PARTS.P# = ORDERS.P#
  3854.  
  3855.  
  3856.  
  3857.  
  3858.  
  3859.                      - 61 -
  3860.  
  3861.     s#    sname        p#    pname        qty
  3862.     -------    ---------------    -------    ---------------    -------
  3863.     1    Smith, John    1    bolt        100
  3864.     1    Smith, John    2    screw        200
  3865.     1    Smith, John    3    screw        300
  3866.     1    Smith, John    4    nut        400
  3867.     1    Smith, John    5    bolt        500
  3868.     1    Smith, John    6    nail        600
  3869.     2    Jones, Ben    1    bolt        100
  3870.     2    Jones, Ben    2    screw        100
  3871.     2    Jones, Ben    6    nail        100
  3872.     3    Barnes, Frank    1    bolt        200
  3873.     3    Barnes, Frank    2    screw        50
  3874.     4    Landon, Mark    3    screw        100
  3875.     4    Landon, Mark    5    bolt        500
  3876.  
  3877.  
  3878. As in the two table join, it makes no difference in which order the
  3879. tables are joined.  The result will always be the same.
  3880.  
  3881.  
  3882. "In" subqueries
  3883.  
  3884. Suppose you want a list of all suppliers who are supplying red parts.
  3885. This query requires two steps, first creating a set containing all red
  3886. parts, then selecting every supplier who supplies a part in the set.
  3887. You must remember to drop the set when the query is finished.  You
  3888. cannot make set TEMP because it would be automatically dropped the
  3889. first time iffunc is called.
  3890.  
  3891.     int iffunc(void *rec)  {    
  3892.       return tlookup(set, "p#", EQ, ((order *)rec)->p, NULL);
  3893.     }
  3894.  
  3895.     set = tproject(STATIC, parts, "p#", "color", EQ, "red", NULL);
  3896.     answer = tprojectif(STATIC, orders, "*s#", iffunc, NULL);
  3897.     tdrop(set);
  3898.  
  3899.     SELECT DISTINCT S# FROM ORDERS WHERE P# IN
  3900.       (SELECT P# FROM PARTS WHERE COLOR = "red")
  3901.  
  3902.  
  3903.     s#
  3904.     -------    
  3905.     1
  3906.     2
  3907.     3
  3908.  
  3909.  
  3910. Multiple subqueries
  3911.  
  3912. To get the name of every supplier who is supplying red parts.  
  3913.  
  3914.  
  3915.  
  3916.  
  3917.  
  3918.  
  3919.                      - 62 -
  3920.  
  3921.     TABLE *set1, *set2;
  3922.  
  3923.     int iffunc1(void *rec)  {    
  3924.       return tlookup(set, "p#", EQ, ((order *)rec)->p, NULL);
  3925.     }
  3926.  
  3927.     int iffunc2(void *rec)  {    
  3928.       return tlookup(set, "s#", EQ, ((sup *)rec)->s, NULL);
  3929.     }
  3930.  
  3931.     set1 = tproject(STATIC, parts, "*p#", "p#", EQ, "red", NULL);
  3932.     set2 = tprojectif(STATIC, orders, "*s#", iffunc1, NULL);
  3933.     tdrop(set1);
  3934.     answer = tselectif(STATIC, sups, iffunc2, NULL);
  3935.     tdrop(set2);
  3936.  
  3937.     SELECT * FROM SUPS WHERE S# IN
  3938.       (SELECT S# FROM ORDERS WHERE P# IN
  3939.         (SELECT P# FROM PARTS WHERE COLOR = "red"))
  3940.  
  3941.  
  3942.     s#    sname         city        status
  3943.     -------    ---------------    ---------------    -------
  3944.     1    Smith, John    Boston        5
  3945.     2    Jones, Ben    New York    10
  3946.     3    Barnes, Frank    Boston        15
  3947.  
  3948.  
  3949. This query is simply a subquery within a subquery, more specifically,
  3950. and "in" subquery within an "in" subquery.  Thus it requires two "..if"
  3951. function calls instead of just one.
  3952.  
  3953. Another way to do this would be to perform this query would be to take
  3954. the answer from the original "in" query, and join it to the sups table.
  3955. Either is a legitimate answer to the query.
  3956.  
  3957.  
  3958. Group By queries
  3959.  
  3960. CRDE performs group by queries with the tgroup function.  tgroup
  3961. requires you to write a special group function which works with tgroup
  3962. to create the answer table.  First, an example is shown to illustrate
  3963. what a common tgroup query will look like.  Suppose you want a list of
  3964. the number of suppliers, per city.
  3965.  
  3966.  
  3967.  
  3968.  
  3969.  
  3970.  
  3971.  
  3972.  
  3973.  
  3974.  
  3975.  
  3976.  
  3977.  
  3978.  
  3979.                      - 63 -
  3980.  
  3981.     int groupfunc(void *in, void *out, int first)  {
  3982.       typedef struct {
  3983.         char city[26];
  3984.         int count;
  3985.       } ans;
  3986.  
  3987.       switch (first) {
  3988.         case 1 :
  3989.           strcpy(((ans *)out)->city, ((sup *)in)->city);
  3990.           ((ans *)out)->count = 0;
  3991.  
  3992.         case 0 :
  3993.           ((ans *)out)->count++;
  3994.           break;
  3995.       }
  3996.       return 1;
  3997.     }
  3998.  
  3999.     answer = tgroup(STATIC, sups, "city", "city c26, count i",
  4000.       groupfunc);
  4001.  
  4002.     SELECT CITY,COUNT(S#) FROM S GROUP BY CITY
  4003.  
  4004.  
  4005.     city        count
  4006.     ---------------    -------
  4007.     Auburn        1
  4008.     Boston        2
  4009.     New York    2
  4010.  
  4011.  
  4012. tgroup is declared in crde.h as
  4013.  
  4014.     TABLE *tgroup(char *name, TABLE *t, char *cl, char *td,
  4015.       int (*summary)(void *in, void *out, int first));
  4016.  
  4017. The cl parameter in tgroup lists the column(s) to group on. You can
  4018. group on up to 8 columns with tgroup.  All rows in the table with the
  4019. same values in the columns in cl belong to the same group.  In the
  4020. above example, rows are grouped by city.  Thus all rows with the same
  4021. city belong to the same group.
  4022.  
  4023. td is a table descriptor describing the resulting table.  td  may be
  4024. anything you wish.  summary is responsible for creating the table
  4025. described by td.
  4026.  
  4027. summary is a user-defined group function which actually generates the
  4028. result table.  Group functions must follow a specific set of rules to
  4029. work correctly.
  4030.  
  4031.  
  4032. Writing grouping functions
  4033.  
  4034. summary should have a declaration similar to 
  4035.  
  4036.     int summary(void *in, void *out, int first);  
  4037.  
  4038.  
  4039.                      - 64 -
  4040.  
  4041. Every row in each group is passed to summary and is referenced by in.
  4042. The information in used to calculate the output row for the group.  out
  4043. is a pointer to the output row for a particular group.  summary is
  4044. responsible for building out.  first is a parameter used to determine
  4045. whether a row is the first or last element in the group.  The
  4046. information is used to determine when to initialize the group and
  4047. whether to include the group in the answer table.  The following
  4048. describes how tgroup uses summary to generate the answer table.
  4049.  
  4050.     1. tgroup calls summary with first == 1.  in points to the first
  4051.     row in the group.  summary should initialize out at this point.
  4052.  
  4053.     2. tgroup calls summary with first == 0 for every other row in
  4054.     the group.  Each row is accessed through the pointer in.
  4055.     summary should update any calculated columns in out based
  4056.     on each row in the group.
  4057.  
  4058.     3. After every row in the group has been passed to summary,
  4059.     tgroup calls summary once more time with first == -1.
  4060.     summary should perform any final calculations (now that all
  4061.     rows in the group have been processed), and do any cleanup
  4062.     necessary.  out should now contain the values which will
  4063.     appear in the answer table.  However, summary can also decide
  4064.     whether or not it wants to include out in the answer table.
  4065.     If summary returns > 0 then tgroup will include out.  If
  4066.     summary returns 0 then tgroup will not include the row.
  4067.  
  4068. tgroup repeats step 1-3 for every group in the table.  In steps 1. and
  4069. 2., summary  should return a value >= 0 if no errors occurred.
  4070. Regardless of the value of first, if the summary  returns a value < 0,
  4071. tgroup aborts and returns NULL.  terrno will be set to the value
  4072. returned by summary.  A well designed summary should include error-
  4073. handling and perform any necessary cleanup if an error occurs.
  4074.  
  4075. To now describe the operation of  the first example, tgroup groups the
  4076. table by "city".  The resulting table will contain the city of the
  4077. group and the number of suppliers in that city ("city c26, count i").
  4078. tgroup then starts calling groupfunc for each row in each group to
  4079. generate the answer table.
  4080.  
  4081. groupfunc accepts the first row  of the first group which happens to be
  4082. "Auburn".  first equals 1 so control goes to the first case in the switch
  4083. statement
  4084.  
  4085.     case 1 :
  4086.       strcpy(((ans *)out)->city, ((sup *)in)->city);
  4087.       ((ans *)out)->count = 0;
  4088.  
  4089. out  is initialized to "city "= "Auburn" and "count" = 0.  Notice that
  4090. out is typecast with ans. ans must be a structure typedef which is
  4091. identical to the answer table being generated.  in, of course, must be
  4092. typecast by sup.
  4093.  
  4094. Since in  is also the first row in the group, we have to update "count"
  4095. in out.  So control passes on to the next case in the switch statement
  4096. (since there is no break statement)
  4097.  
  4098.  
  4099.                      - 65 -
  4100.  
  4101.     case 0 :
  4102.       ((ans *)out)->count++;
  4103.       break;
  4104.  
  4105. case 0, which is used by every other row in the group besides the first,
  4106. increments the "count" in out.  So "count" now equals 1.  Control now
  4107. passes to the
  4108.  
  4109.     return 1;
  4110.  
  4111. statement, and groupfunc returns successfully to tgroup.
  4112.  
  4113. tgroup normally would processes every other row in the group (all those
  4114. suppliers who live in Auburn) to groupfunc, with first set to 0.
  4115. groupfunc would then increment "count" in out for each row.  However,
  4116. there happens to be only one row in the sups table who lives in Auburn,
  4117. so tgroup immediately calls groupfunc with first set to -1.  groupfunc
  4118. simply returns 1 in this case, indicating that tgroup should insert out
  4119. should in the answer table.  At this point, the answer table looks like
  4120.  
  4121.  
  4122.     city        count
  4123.     ---------------    -------
  4124.     Auburn        1
  4125.  
  4126.  
  4127. tgroup now moves on to the next group, which happens to be "Boston".
  4128. The first row in the group (either John Smith or Frank Barnes,
  4129. whichever happens to be picked first by tgroup) is sent to groupfunc;
  4130. first of course, equals 1.  out is initialized to "city" = "Boston",
  4131. "count" = 1.  tgroup calls groupfunc again with the other row in the
  4132. group, with first set to 0.  "count" is incremented to 2.  groupfunc
  4133. is called once more with first set to -1, which immediatly returns and
  4134. the new out  is inserted.  The answer table now looks like
  4135.  
  4136.  
  4137.     city        count
  4138.     ---------------    -------
  4139.     Auburn        1
  4140.     Boston        2
  4141.  
  4142.  
  4143. tgroup repeats the process for the final group, "New York", which also
  4144. has two suppliers.  After the final group is completed, tgroup returns
  4145. the generated table as its result.
  4146.  
  4147.  
  4148.     city        count
  4149.     ---------------    -------
  4150.     Auburn        1
  4151.     Boston        2
  4152.     New York    2
  4153.  
  4154.  
  4155.  
  4156.  
  4157.  
  4158.  
  4159.                      - 66 -
  4160.  
  4161. Example #2.
  4162.  
  4163. Calculate the sum of all parts ordered by each supplier. 
  4164.  
  4165.     int calcsum(void *in, void *out, int first)
  4166.     {
  4167.       typedef struct {
  4168.         int s;
  4169.         int sum;
  4170.       } group;
  4171.  
  4172.       switch (first) {
  4173.         case 1 :
  4174.           ((group *)out)->s   = ((order *)in)->s;
  4175.           ((group *)out)->sum = 0;
  4176.  
  4177.         case 0 :
  4178.           ((order *)out)->sum += ((order *)in)->quant;
  4179.           break;
  4180.       }
  4181.       return 1;
  4182.     }
  4183.  
  4184.     answer = tgroup(STATIC, orders, "s#", "s# i, sumofqty i", calcsum);
  4185.  
  4186.     SELECT S#,SUM(QTY) FROM ORDERS GROUP BY S#
  4187.  
  4188.     s#    sumofqty
  4189.     -------    -----------
  4190.     1    2100
  4191.     2    300
  4192.     3    250
  4193.     4    600
  4194.  
  4195.  
  4196. Example #2 is similar to the first example except that a column is
  4197. summed instead of counted.
  4198.  
  4199.  
  4200. Example #3.
  4201.  
  4202. This example demonstrate how the -1 flag in first can be used to find
  4203. a group average.
  4204.  
  4205.     int calcavg(void *in, void *out, int first)
  4206.     {
  4207.       typedef struct {
  4208.         int s;
  4209.         double avg;
  4210.       } group;
  4211.       static int i;
  4212.  
  4213.       switch (first) {
  4214.         case 1 :
  4215.           ((group *)out)->s   = ((order *)in)->s;
  4216.           ((order *)out)->avg = 0.0;
  4217.           i = 0;
  4218.  
  4219.                      - 67 -
  4220.  
  4221.         case 0 :
  4222.           ((group *)out)->avg += ((order *)in)->quant;
  4223.           i++;
  4224.           break;
  4225.  
  4226.         case -1 :
  4227.           ((order *)out)->avg /= i;
  4228.           break;
  4229.       }
  4230.       return 1;
  4231.     }
  4232.  
  4233.     answer = tgroup(STATIC, orders, "s#", "s# i, sumofqty i", calcavg);
  4234.  
  4235.     SELECT S#,AVG(QTY) FROM ORDERS GROUP BY S
  4236.  
  4237.  
  4238. calcavg is identical to calcsum except that it contains the extra case
  4239.  
  4240.     case -1:
  4241.       ((order *)out)->avg /= i;
  4242.       break;
  4243.  
  4244. This calculates the average of the group by dividing the sum of all
  4245. the orders by the number of orders made.  Note that this can only be
  4246. done by taking advantage of the fact that tgroup calls calcavg with
  4247. first == -1 at the end of each group.
  4248.  
  4249. As you can see, group functions are very similar to the if functions
  4250. described in Chapter 7, and provide an almost endless range of
  4251. possibilities.  Like if functions, group functions place the host
  4252. table in a special read-only mode, so that you may not modify it.
  4253. However, no other restrictions are imposed by group functions.
  4254.  
  4255.  
  4256. Group Having queries
  4257.  
  4258. You can simulate the HAVING clause of an SQL statement by simply
  4259. placing the results of a tgroup within a tselect function.  For
  4260. example, suppose you want only those rows generating in example #2
  4261. having a sum > 500.
  4262.  
  4263.     answer = tselect(STATIC,
  4264.       tgroup(TEMP, orders, "s#", "s# i, sumofqty i", calcsum),
  4265.       "sumofqty", GT, 500, NULL
  4266.     );
  4267.  
  4268.     SELECT S#,SUM(QTY) GROUP BY S# HAVING SUM(QTY) > 500
  4269.  
  4270.  
  4271.     s#    sumofqty
  4272.     -------    -----------
  4273.     1    2100
  4274.     4    600
  4275.  
  4276.  
  4277.  
  4278.  
  4279.                      - 68 -
  4280.  
  4281. You can also specify a HAVING clause in your summary function by having
  4282. it return 1 or 0 when first == -1 depending upon whether you want to
  4283. include the row or not.  For example, you could duplicate the above
  4284. query without the tselect statement by adding the following case to
  4285. calcsum
  4286.  
  4287.     case -1 :
  4288.       return ((group *)out)->sum > 500;
  4289.  
  4290.  
  4291. Set Group queries
  4292.  
  4293. Grouping functions, like "if" functions, provide querying possibilities
  4294. which go far beyond even the capabilities of even high level query
  4295. languages like SQL.  One such type of query is one which groups rows
  4296. based upon sets of data.  A set of data might be something like all red
  4297. parts.  With a set related query you could then ask questions like
  4298.   
  4299.     Which suppliers supply no red parts?  
  4300.     Which suppliers supply only red parts?  
  4301.     Which suppliers supply every red part?  
  4302.     Which suppliers supply exactly red parts?
  4303.  
  4304.  
  4305. Which suppliers supply no red parts?  
  4306.  
  4307.     int groupno(void *in, void *out, int first)
  4308.     {
  4309.       static int flag;
  4310.  
  4311.       switch (first) {
  4312.         case  1 :
  4313.           *(int *)out = ((order *)in)->s; flag = 0;
  4314.         case  0 :
  4315.           if (tlookup(set, "p#", EQ, ((order *)in)->p, NULL))
  4316.         flag = 1;
  4317.           break;
  4318.         case -1 :
  4319.           return !flag;
  4320.       }
  4321.     }
  4322.  
  4323.     set = tproject(STATIC, parts, "p#", "color", EQ, "red", NULL);
  4324.     answer = tgroup(STATIC, orders, "s#", "s# i", groupno);
  4325.       tdrop(set);
  4326.  
  4327.  
  4328.     s#
  4329.     -------
  4330.     4
  4331.     
  4332.  
  4333. groupno requires that a set be created before the queried is executed.
  4334. You must remember to remove the set afterwards.
  4335.  
  4336.  
  4337.  
  4338.  
  4339.                      - 69 -
  4340.  
  4341. You may have noticed that the answer to the groupno query does not
  4342. contain supplier #5!  Is there an error in groupno?  Well, yes and
  4343. no.  groupno  does select only those suppliers which supply no red
  4344. parts, but only those which supply at least one part.  Any suppliers
  4345. who do not supply any parts will not appear in the groupno query.
  4346. This is because the group set occurs on the orders table, not the sups
  4347. table.  Fortunately, grouponly, groupevery, and groupexactly do not
  4348. suffer from this problem since they only work with suppliers who do
  4349. supply parts.
  4350.  
  4351.  
  4352. What suppliers supply only red parts?  
  4353.  
  4354.     int grouponly(void *in, void *out, int first)
  4355.     {
  4356.       static TABLE *gset;
  4357.       int i;
  4358.  
  4359.       switch (first) {
  4360.         case  1 :
  4361.           gset = tcreat(STATIC, "*p# i");
  4362.           *(int *)out = ((order *)in)->s;
  4363.         case  0 :
  4364.           tload(gset, ((order *)in)->p);
  4365.           break;
  4366.         case -1 :
  4367.           i = trows(tdiff(TEMP, gset, set)) == 0;
  4368.           tdrop(gset);
  4369.           return i;
  4370.       }
  4371.       return 1;
  4372.     }
  4373.  
  4374.     set = tproject(STATIC, parts, "p#", "color", EQ, "red", NULL);
  4375.     answer = tgroup(STATIC, orders, "s#", "s# i", grouponly);
  4376.     tdrop(set);
  4377.  
  4378.  
  4379.     s#
  4380.     -------
  4381.     2
  4382.     3
  4383.  
  4384.  
  4385. Like all the group set queries, grouponly requires that a set containing
  4386. all red parts be created before the actual query is performed.
  4387. grouponly may be the most complex query we have seen so far.  It
  4388. requires that the group function itself generate a table, then perform
  4389. a set comparison.  grouponly, groupevery and groupexactly are very
  4390. similar in design.
  4391.  
  4392.  
  4393.  
  4394.  
  4395.  
  4396.  
  4397.  
  4398.  
  4399.                      - 70 -
  4400.  
  4401. This is how it works.  grouponly performs the query by creating a set
  4402. of all the parts ordered by suppliers in that particular group, gset.
  4403. grouponly then compares gset to set with the tdiff  function.  If the
  4404. difference of gset - set yields a table with 0 rows, that means that
  4405. gset contained only those parts found in set.  Hence, every part
  4406. supplied by that supplier (gset) was a red part (set).
  4407.  
  4408. When grouponly is called with first equals 1, it creates the gset table.
  4409. It only needs to contain one column, p#, and it fact it must, since it
  4410. needs to be compatible with set so that they will work with tdiff.
  4411. out is also initalized at this point to the current group's supplier #.
  4412. Since gset is declared within grouponly it must be declared as static,
  4413. otherwise the table would disappear every time grouponly returned to
  4414. tgroup.  
  4415.  
  4416. grouponly then loads gset with the part#'s ordered by that supplier.
  4417. When every row in the group has been processed, gset will contain
  4418. every part# ordered by that supplier.  Notice that the p# column in
  4419. gset is declared as the primary key (as is set). This is not required.
  4420. grouponly will work fine is either or both are not keyed.  However, on
  4421. larger tables, creating the sets with a primary key will most likely
  4422. improve performance. 
  4423.  
  4424. Now the tricky part.  grouponly now must compare the two sets.  It does
  4425. this after the entire group has been processed, when first equals -1.
  4426. tdiff is used to compare gset to set.  As explained above, if the
  4427. difference between the two yields a table with 0 rows, then grouponly
  4428. returns 1, meaning tgroup should include out in the answer table,
  4429. otherwise, grouponly returnes 0.  Importantly, grouponly also drops
  4430. the gset table it created.   If it didn't the table would lie around
  4431. in memory until the program terminated.
  4432.  
  4433.  
  4434. What suppliers supply every red part?  
  4435.  
  4436.     int groupevery(void *in, void *out, int first)
  4437.     {
  4438.       static TABLE *gset;
  4439.       int i;
  4440.  
  4441.       switch (first) {
  4442.         case  1 :
  4443.           gset = tcreat(STATIC, "*p# i");
  4444.           *(int *)out = ((order *)in)->s;
  4445.         case  0 :
  4446.           tload(gset, ((order *)in)->p);
  4447.           break;
  4448.         case -1 :
  4449.           i = trows(tdiff(TEMP, set, gset)) == 0;
  4450.           tdrop(gset);
  4451.           return i;
  4452.       }
  4453.       return 1;
  4454.     }
  4455.  
  4456.  
  4457.  
  4458.  
  4459.                      - 71 -
  4460.  
  4461.     set = tproject(STATIC, parts, "p#", "color", EQ, "red", NULL);
  4462.     answer = tgroup(STATIC, orders, "s#", "s# i", groupevery);
  4463.     tdrop(set);
  4464.  
  4465.  
  4466.     s#
  4467.     -------
  4468.     1
  4469.     2
  4470.  
  4471. groupevery is identical to grouponly except that in the tdiff statement,
  4472. gset and set are switched around.  Since set contains every red part,
  4473. if the difference of set and gset is 0, then gset  must also contain
  4474. every red part.  gset can also contain parts that are not red, just as
  4475. long as it contains every one that is.
  4476.  
  4477.  
  4478. Which suppliers supply exactly red parts?  
  4479.  
  4480.     int groupexactly(void *in, void *out, int first)
  4481.     {
  4482.       static TABLE *gset;
  4483.       int i;
  4484.  
  4485.       switch (first) {
  4486.         case  1 :
  4487.           gset = tcreat(STATIC, "*p# i");
  4488.           *(int *)out = ((order *)in)->s;
  4489.         case  0 :
  4490.           tload(gset, ((order *)in)->p);
  4491.           break;
  4492.         case -1 :
  4493.           i = trows(tdiff(TEMP, gset, set)) == 0 &&
  4494.           trows(tdiff(TEMP, set, gset)) == 0;
  4495.           tdrop(gset);
  4496.           return i;
  4497.       }
  4498.       return 1;
  4499.     }
  4500.  
  4501.     set = tproject(STATIC, parts, "p#", "color", EQ, "red", NULL);
  4502.     answer = tgroup(STATIC, orders, "s#", "s# i", groupexactly);
  4503.     tdrop(set);
  4504.  
  4505.  
  4506.     s#
  4507.     -------
  4508.     2
  4509.  
  4510.  
  4511.  
  4512.  
  4513.  
  4514.  
  4515.  
  4516.  
  4517.  
  4518.  
  4519.                      - 72 -
  4520.  
  4521. groupexactly is a cross between grouponly and groupevery.  Common
  4522. sense tells use this is true.  For gset to exactly match set it must
  4523. contain every part in set, and only those parts in set.  Thus
  4524. groupexactly tests for both.  If either is false then the sets are
  4525. not exactly alike.  Only one supplier, #2, supplies exactly red parts.
  4526. Notice the supplier #2 is the only supplier found in both the grouponly
  4527. result and the groupevery result.
  4528.  
  4529.  
  4530. Optimizing Queries
  4531.  
  4532. As queries become more and more complex, the variety of ways in which
  4533. they can be accomplished increases.  But which way is best?  By
  4534. following some general guidelines, you can help improve the performance
  4535. times of your queries.  The following is a list of functions in the
  4536. order or level at which they should pear in a query.  When performing
  4537. queries which combine two or more of these functions, the ones at the
  4538. bottom of the list should be performed before, or within, those at the
  4539. top of the list.  
  4540.  
  4541.     tsort   
  4542.     tunion    
  4543.     tjoin     
  4544.     tintersect      
  4545.     tdiff    
  4546.        tprojectif   
  4547.     tselectif          
  4548.     tproject
  4549.     tselect 
  4550.  
  4551. If you want the results of a query sorted, tsort should always be the
  4552. last or outermost function.  It doesn't make any sense to nest tsort
  4553. within another query.  Also, use temporary tables (TEMP and STATIC)
  4554. whenever possible.  CRDE can often hold the intermediate results of a
  4555. query completely in memory, thus improving performance.
  4556.  
  4557.  
  4558.  
  4559.  
  4560.  
  4561.  
  4562.  
  4563.  
  4564.  
  4565.  
  4566.  
  4567.  
  4568.  
  4569.  
  4570.  
  4571.  
  4572.  
  4573.  
  4574.  
  4575.  
  4576.  
  4577.  
  4578.  
  4579.                      - 73 -
  4580.  
  4581.  
  4582.  
  4583.  
  4584.  
  4585.  
  4586. Chapter 12
  4587.  
  4588. Creating Reports
  4589.  
  4590.  
  4591.  
  4592. Single-table reports
  4593.  
  4594. In the last section we discussed how to use CRDE to generate queries
  4595. and ask questions about the data in your database.  Often you will
  4596. want to keep the results of a query permanent, on paper.  The CRDE
  4597. function usually used for generating reports is tscanif.  tscanif
  4598. doesn't do anything accept call its if function for every row in the
  4599. table.  If you tell the if function to print out those rows, you can
  4600. create a hard copy report of your tables and queries.  
  4601.  
  4602. This simple example prints out a report on the sups table.  
  4603.  
  4604.     int line, page;  
  4605.     char *title;  
  4606.  
  4607.     #define LINESPERPAGE 54  
  4608.  
  4609.     int ReportSups1(void *rec)  
  4610.     {    
  4611.       /* print report header at top of page */
  4612.       if (line == 0) {
  4613.       fprintf(stdprn, "%*s\n", 40 + strlen(title), title);
  4614.       fprintf(stdprn, "%41s %d\n\n", "PAGE", page);
  4615.       fprintf(stdprn, "  s#            sname           \n");
  4616.       fprintf(stdprn, "------ "  "-------------------------\n");
  4617.         line += 5;
  4618.       }
  4619.  
  4620.       /* print a row */
  4621.       fprintf(stdprn, "%6d %-25s\n", ((sup*)rec)->s,
  4622.         ((sup *)rec)->sname);
  4623.  
  4624.       /* test for end of page */
  4625.       if (line % LINESPERPAGE == 0) {
  4626.         fprintf(stdprn, "\12");
  4627.         line = 0;
  4628.       }
  4629.  
  4630.       return 1;
  4631.     }
  4632.  
  4633.     line = 0; page = 1; 
  4634.     title = "SUPPLIER REPORT";  
  4635.     tscanif(sups, ReportSups1, NULL);
  4636.  
  4637.  
  4638.  
  4639.                      - 74 -
  4640.  
  4641. The above ReportSups1 report is generic enough to be used in a variety
  4642. in situations, namely any query which generates a subset of the sups
  4643. table.  For example, this portion of code uses the ReportSups1 to
  4644. generate a report listing all those suppliers with an above average
  4645. status.  
  4646.  
  4647.     double d;  
  4648.  
  4649.     taverage(sups, "status", &d);  
  4650.     answer = tsort(STATIC,
  4651.       tselect(TEMP, sups, "status", GT, (int)d, NULL),
  4652.       "sname"
  4653.     );
  4654.  
  4655.     line = 0; page = 1; 
  4656.     title = "SUPPLIERS WITH ABOVE AVERAGE STATUS";  
  4657.     tscanif(answer, ReportSups1, NULL);
  4658.  
  4659. The above report uses the same report function, ReportSups1, to output
  4660. a list of all suppliers with above average status, sorted by supplier
  4661. name.  Of course, since tscanif is a search-related function, simple
  4662. queries can be performed by tscanif itself.  For example to list only
  4663. those suppliers in Boston  
  4664.  
  4665.     line = 0; page = 1; 
  4666.     title = "SUPPLIERS IN BOSTON";  
  4667.     tscanif(sups, ReportSups1, "city, EQ, "Boston", NULL);
  4668.  
  4669. However, since most reports either are more complex, or require that
  4670. the report be sorted, you will generally find it more common to create
  4671. a separate query rather than filter a table through tscanif.
  4672.  
  4673.  
  4674. Multi-table reports
  4675.  
  4676. You can produce multi-table reports with CRDE just as easily as single-
  4677. table reports.  There are two basic methods for mutli-table reports:
  4678. the join method, and the lookup method.
  4679.  
  4680.  
  4681. The join method
  4682.  
  4683. The join method requires that you create the multi-table report by
  4684. joining all the linked tables together into a single table, and then
  4685. produce the report.  For example, suppose you wanted to create a
  4686. verbose orders report containing all orders with both supplier and
  4687. part information.
  4688.  
  4689.  
  4690.  
  4691.  
  4692.  
  4693.  
  4694.  
  4695.  
  4696.  
  4697.  
  4698.  
  4699.                      - 75 -
  4700.  
  4701.     int line, page;
  4702.     char *title;
  4703.  
  4704.     #define LINESPERPAGE 54
  4705.  
  4706.     int ReportOrders1(void *rec)
  4707.     {
  4708.       /* this typedef must exactly match joined table's structure */
  4709.       typedef struct {
  4710.         int s;
  4711.         char sname[26];
  4712.         char city[26];
  4713.         int status;
  4714.         int p;
  4715.         char pname[16];
  4716.         char color[11];
  4717.         int qty;
  4718.       } spo;
  4719.  
  4720.       /* print report header at top of page */
  4721.       if (line == 0) {
  4722.         fprintf(stdprn, "%*s\n", 40 + strlen(title), title);
  4723.         fprintf(stdprn, "%41s %d\n\n", "PAGE", page);
  4724.         fprintf(stdprn, "s#     sname                   "
  4725.           " city                     status p#     pname "
  4726.           " color qty\n");
  4727.         fprintf(stdprn, "------ ------------------------- "
  4728.           "--------------- ------ --------------- ---------- "
  4729.           "------\n");
  4730.         line += 5;
  4731.       }
  4732.  
  4733.       /* print an order */
  4734.       fprintf(stdprn, "%6d %-25s %-25s %6d %6d %-15s %-10s %6d\n",
  4735.         ((spo *)rec)->s,
  4736.         ((spo *)rec)->sname,
  4737.         ((spo *)rec)->city,
  4738.         ((spo *)rec)->status,
  4739.         ((spo *)rec)->p,
  4740.         ((spo *)rec)->pname,
  4741.         ((spo *)rec)->color,
  4742.         ((spo *)rec)->qty
  4743.       );
  4744.  
  4745.       /* if end of page, form feed */
  4746.       if (line % LINESPERPAGE == 0) {
  4747.         fprintf(stdprn, "\12");
  4748.         line = 0;
  4749.       }
  4750.  
  4751.       return 1;
  4752.     }
  4753.  
  4754.  
  4755.  
  4756.  
  4757.  
  4758.  
  4759.                      - 76 -
  4760.  
  4761.     answer = tjoin(TEMP, sups, "s#",
  4762.       tjoin(TEMP, part, "p#", orders, "p#","s#,p#,pname,color,qty"),
  4763.       "s#",
  4764.       "s#,sname,city,status,p#,pname,color, qty"
  4765.     );
  4766.  
  4767.     line = 0; page = 1;
  4768.     title = "ORDERS REPORT";
  4769.     tscanif(answer, ReportOrders1, NULL);
  4770.  
  4771.  
  4772. The lookup method
  4773.  
  4774. The lookup method takes a different approach.  It scans each row in
  4775. the orders table, then uses the tget  function to retrieve supplier
  4776. and part information.  
  4777.  
  4778.     int line, page;
  4779.     char *title;
  4780.  
  4781.     int ReportOrders2(void *rec)
  4782.     {
  4783.       sup srec;
  4784.       part prec;
  4785.  
  4786.       /* print report header at top of page */
  4787.       if (line == 0) {
  4788.         fprintf(stdprn, "%*s\n", 40 + strlen(title), title);
  4789.         fprintf(stdprn, "%41s %d\n\n", "PAGE", page);
  4790.         fprintf(stdprn, "s#     sname                   "
  4791.           " city                     status p#     pname "
  4792.           " color qty\n");
  4793.         fprintf(stdprn, "------ ------------------------- "
  4794.           "--------------- ------ --------------- ---------- "
  4795.           "------\n");
  4796.         line += 5;
  4797.       }
  4798.  
  4799.       /* look up supplier */
  4800.       i = tget(sups, &srec, 1, "s#", EQ, ((order *)rec)->s, NULL);
  4801.       if (i < 0) return i;
  4802.  
  4803.       /* look up part */
  4804.       i = tget(parts,  &prec, 1, "s#", EQ, ((order *)rec)->p, NULL);
  4805.       if (i < 0) return i;
  4806.  
  4807.       /* print an order */
  4808.       fprintf(stdprn, "%6d %-25s %-25s %6d %6d %-15s %-10s %6d\n",
  4809.         ((sup *)srec)->s,
  4810.         ((sup *)srec)->sname,
  4811.         ((sup *)srec)->city,
  4812.         ((sup *)srec)->status,
  4813.         ((part *)prec)->p,
  4814.         ((part *)prec)->pname,
  4815.         ((part *)prec)->color,
  4816.         ((order *)rec)->qty
  4817.       );
  4818.  
  4819.                      - 77 -
  4820.  
  4821.       /* if end of page, form feed */
  4822.       if (line % LINESPERPAGE == 0) {
  4823.         fprintf(stdprn, "\12");
  4824.         line = 0;
  4825.       }
  4826.  
  4827.       return 1;
  4828.     }
  4829.  
  4830.     line = 0; page = 1;
  4831.     title = "ORDERS REPORT";
  4832.     tscanif(answer, ReportOrders1, NULL);
  4833.  
  4834. The method presented with tscanif is not the only possibility for
  4835. creating reports, although it is probably the most common.  tget could
  4836. be used to place rows in an array where they could be manipulated
  4837. freely.  tgroup is also a possibility for reporting, using its
  4838. automatic grouping abilities to output reports based upon groups
  4839. and subtotals.  In reality, the possibilities are endless.  CRDE's
  4840. intimate relationship with the C language gives it the flexibility to
  4841. create even the most complex reports.
  4842.  
  4843.  
  4844.  
  4845.  
  4846.  
  4847.  
  4848.  
  4849.  
  4850.  
  4851.  
  4852.  
  4853.  
  4854.  
  4855.  
  4856.  
  4857.  
  4858.  
  4859.  
  4860.  
  4861.  
  4862.  
  4863.  
  4864.  
  4865.  
  4866.  
  4867.  
  4868.  
  4869.  
  4870.  
  4871.  
  4872.  
  4873.  
  4874.  
  4875.  
  4876.  
  4877.  
  4878.  
  4879.                      - 78 -
  4880.  
  4881.  
  4882.  
  4883.  
  4884.  
  4885.  
  4886. Chapter  13
  4887.  
  4888. Maintaining Database Integrity
  4889.  
  4890.  
  4891.  
  4892. The term "database integrity" refers to the accuracy or correctness
  4893. of data in the database.  Making sure the data in a database is correct
  4894. is a very important job.  Many data managers, including CRDE, provide
  4895. assistance to support database integrity.  Integrity in relational
  4896. databases can be broken down into three basic categories:  entity
  4897. integrity, domain integrity, and referential integrity.
  4898.  
  4899.  
  4900. Entity integrity
  4901.  
  4902. Entity integrity refers to the fact that the key values in a
  4903. relational table should be unique in that table.  This sort of
  4904. integrity is handled automatically by CRDE whenever you define a
  4905. primary key on a table.  No matter what operations you perform upon
  4906. a keyed table, CRDE always guarantees the uniqueness of its key values
  4907. within the table.  CRDE allows you to get around the entity integrity
  4908. rule by creating non-keyed tables.  Non-keyed tables can be useful in
  4909. a number of situations.
  4910.  
  4911.  
  4912. Domain integrity
  4913.  
  4914. Domain integrity refers to the correctness of data in a particular
  4915. column.  Every column has a practical set of values, a domain, which
  4916. it should hold.  For example, the domain for the "age" column is
  4917. probably an integer between 1 and 99, the domain for the "sex" column
  4918. is either "Male" or "Female", etc.  A data management system which
  4919. supports domain integrity would not allow you to enter 800 into the
  4920. "age" column, for example.
  4921.  
  4922. CRDE does not support domain integrity directly, i.e. there are no CRDE
  4923. functions which you can call to set the domain of a particular column.
  4924. However domain integrity is relatively simple to implement using C.
  4925. For example, you might write a function which would screen the input
  4926. of suppliers so that the "status" column contained a value between 0
  4927. and 99.
  4928.  
  4929.     int InsertSup(sup *s) 
  4930.     {
  4931.       if (s->status < 1 || s->status > 99)
  4932.         return 0;
  4933.       return tinsert(sups, s, 1);
  4934.     }
  4935.  
  4936.  
  4937.  
  4938.  
  4939.                      - 79 -
  4940.  
  4941. InsertSup returns 1 if the row was inserted, or 0 if it was not.  You
  4942. might want to improve InsertSup by insuring that all text columns in
  4943. the row are set to upper case.
  4944.  
  4945.     int InsertSup(sup *s)
  4946.     {
  4947.       if (s->status < 1 || s->status > 99)
  4948.         return 0;
  4949.       strupr(s->sname);
  4950.       strupr(s->city);
  4951.       return tinsert(sups, s, 1);
  4952.     }
  4953.  
  4954.  
  4955. Referential integrity
  4956.  
  4957. Referential integrity refers to the existence of references between
  4958. tables which are linked by key column(s).  For example, the orders
  4959. table has two columns, s# and p#, which are used as references to the
  4960. sups and parts tables respectively.  If there existed an order in the
  4961. orders table like
  4962.  
  4963.     s#    p#    qty
  4964.     -------    -------    -------
  4965.     1    1    100
  4966.  
  4967.  
  4968. there must also exists a supplier #1 in the sups table and a part #1
  4969. in the parts table otherwise the order makes no sense.
  4970.  
  4971. Although CRDE does not support referential integrity directly, its
  4972. high level functions make the task almost trivial.
  4973.  
  4974.  
  4975. Foreign keys and primary keys
  4976.  
  4977. The concept of a foreign key  is the basis for referential integrity.
  4978. As we know, a primary key consists of columns in a table which may not
  4979. contain duplicate values.  A foreign key is/are column(s) in another
  4980. table which reference the table's primary key.  For example, the "s#"
  4981. column in the orders table is a foreign key to the sups table because
  4982. it references the "s#" column in sups.  Likewise, the "p#" column is a
  4983. foreign key to the parts table.  For refential integrity to remain
  4984. intact, there must always exists a primary key for each foreign key in
  4985. a database.
  4986.  
  4987.  
  4988. Referential integrity and inserting
  4989.  
  4990. When you insert rows into a table, you want to be sure that any foreign
  4991. key columns have references.  This is easily handled with CRDE's tlookup
  4992. function.  The following function inserts attempts to insert a row into
  4993. the orders table with full referential checking.  Lookups are made to
  4994. insure that the order's supplier # and part # are valid.
  4995.  
  4996.  
  4997.  
  4998.  
  4999.                      - 80 -
  5000.  
  5001.     int InsertOrder(order *o)  
  5002.     {    
  5003.       int  i;
  5004.  
  5005.       i = tlookup(sups, "s#", EQ, o->s, NULL);
  5006.       if (i > 0) {
  5007.         i = tlookup(parts, "p#", EQ, o->p, NULL);
  5008.         if (i > 0)
  5009.           return tinsert(orders, &o, 1);
  5010.       }
  5011.       return i;
  5012.     }
  5013.  
  5014. InsertOrder uses the tlookup function to insure that referential
  5015. integrity will remain intact when the order is inserted.  InsertOrder
  5016. contains full error-checking, and is a good model of how to use CRDE
  5017. functions in real applications.  InsertOrder  returns 1 if successful,
  5018. or 0 if referential integrity would be comprimised.  If an error
  5019. occurs (like a disk seek error), InsertOrder returns the correct error
  5020. code of the offending error.
  5021.  
  5022.  
  5023. Referential integrity and deleting
  5024.  
  5025. There is a similar problem when deleting rows from tables.  Care must
  5026. be taken so that a reference for some table is not accidentally deleted.
  5027. If you want to delete a supplier from the sups table, you must be sure
  5028. that referential integrity is maintained with the orders table.  The
  5029. following demonstrates four different methods to implement referential
  5030. integrity when deleting a supplier.
  5031.  
  5032.     int DeleteSup1(int supno)
  5033.     {    
  5034.       int i;
  5035.  
  5036.       i = tlookup(orders, "s#", EQ, supno, NULL);
  5037.       if (i == 0) {
  5038.         return tdelete(sups, "s#", EQ, supno, NULL);
  5039.       }
  5040.       return i < 0 ? i : 0;
  5041.     }
  5042.  
  5043. DeleteSup1 is similar to the SQL command ON DELETE RESTRICT.  It
  5044. restricts you from deleting a supplier if any orders reference it.
  5045. DeleteSup1 returns > 0 if successful, 0 if deletion was restricted,
  5046. or < 0 if an error occurred. 
  5047.  
  5048.     int DeleteSup2(int supno)  
  5049.     {
  5050.       int i;
  5051.  
  5052.       i = tdelete(orders, "s#", EQ, supno, NULL);
  5053.       if (i < 0)
  5054.         return i;
  5055.       return tdelete(sups, "s#", EQ, supno, NULL);
  5056.     }
  5057.  
  5058.  
  5059.                      - 81 -
  5060.  
  5061. DeleteSup2 is similar to the SQL notation ON DELETE CASCADE.  In this
  5062. case, if a supplier is deleted, all its orders are deleted as well.
  5063. DeleteSup2 follows the same return value scheme as DeleteSup1.
  5064.  
  5065.     int DeleteSup3(int supno)
  5066.     {
  5067.       int i;
  5068.  
  5069.       i = tdelete(orders, "s#", -1, NULL, "s#", EQ, supno, NULL);
  5070.       if (i < 0)
  5071.         return i;
  5072.       return tdelete(sups, "s#", EQ, supno, NULL);
  5073.     }
  5074.  
  5075. DeleteSup3  is similar to the SQL notation ON DELETE SET NULL.  If a
  5076. supplier is deleted, the "s#" reference in all its orders are set to
  5077. a special value, -1, marking them as unreferenced orders.  The program
  5078. might use this information to print a report containing all those orders
  5079. made by suppliers since deleted.  DeleteSup3  follows the same return
  5080. value scheme as DeleteSup1.
  5081.  
  5082.     TABLE *deleted;
  5083.  
  5084.     deleted = tborrow("deleted", orders);
  5085.  
  5086.     int DeleteSup4(int supno)
  5087.     {
  5088.       int i;
  5089.  
  5090.       i = tadd(deleted, tselect(TEMP, orders, "s#", EQ, supno, NULL));
  5091.       if (i < 0)
  5092.         return i;
  5093.       i = tdelete(orders, "s#", EQ, supno, NULL);
  5094.       if (i < 0)
  5095.         return i;
  5096.       return tdelete(sups, "s#", EQ, supno, NULL);
  5097.     }
  5098.  
  5099. DeletSup4  is similar to DeleteSup3 except that any deleted orders
  5100. are first saved in a special deleted table, so that they may be
  5101. referenced later.  deleted is created with the identical structure
  5102. or orders, using the tborrow function.  DeleteSup4 follows the same
  5103. return value scheme as DeleteSup1.
  5104.  
  5105. Notice that because referential integrity is not built in, CRDE gives
  5106. you the ability to implement referential integrity in a variety of ways,
  5107. not just limited to the ones shown above.
  5108.  
  5109.  
  5110. Referential integrity and changing.
  5111.  
  5112. Problems with referential integrity can occur when referenced values
  5113. are changed in a table.  This can happen, for example, if a supplier's #
  5114. is changed and the orders table is not kept in sync.
  5115.  
  5116.  
  5117.  
  5118.  
  5119.                      - 82 -
  5120.  
  5121.     int ChangeSup1(int oldno, int newno)
  5122.     {
  5123.       int i;
  5124.  
  5125.       i = tchange(sups, "s#", newno, NULL, "s#", EQ, oldno, NULL);
  5126.       if (i > 0) {
  5127.         i = tchange(orders, "s#", newno, "s#", EQ, oldno, NULL);
  5128.         if (i < 0)
  5129.           return i;
  5130.         return 1;
  5131.       }
  5132.       return  i < 0 ? i : 0;
  5133.     }
  5134.  
  5135. Changing the supplier # however introduces a new variable: you may
  5136. accidently try to change the "s#" to a value which already exists.
  5137. What will tchange do in this case?  If changing a row would violate
  5138. the primary key of a table, tchange will not change the row and returns
  5139. 0, for 0 rows changed.  If tchange can change the row, then it will
  5140. return 1, indicating that the "s#" was changed.
  5141.  
  5142. ChangeSup1  returns 1 if the supplier # was changed, 0 if it could
  5143. not because the new supplier # already existed, or an error code (< 0)
  5144. if an error occurred.
  5145.  
  5146.  
  5147.  
  5148.  
  5149.  
  5150.  
  5151.  
  5152.  
  5153.  
  5154.  
  5155.  
  5156.  
  5157.  
  5158.  
  5159.  
  5160.  
  5161.  
  5162.  
  5163.  
  5164.  
  5165.  
  5166.  
  5167.  
  5168.  
  5169.  
  5170.  
  5171.  
  5172.  
  5173.  
  5174.  
  5175.  
  5176.  
  5177.  
  5178.  
  5179.                      - 83 -
  5180.  
  5181.  
  5182.  
  5183.  
  5184.  
  5185.  
  5186. Chapter 14
  5187.  
  5188. Performing Transactions
  5189.  
  5190.  
  5191. CRDE's transaction tracking features
  5192.  
  5193. CRDE provides complete transaction tracking facilities.  A transaction
  5194. is a series of changes made to a table.  CRDE allows you to track
  5195. transactions on a table by table basis.
  5196.  
  5197.     int ttransact(TABLE *t);  
  5198.     int trollback(TABLE *t);  
  5199.     int tcommit(TABLE *t);  
  5200.     int tmark(TABLE *t);
  5201.  
  5202. The ttransact function begins a transaction on a table.  Once ttransact
  5203. is called, all changes made to the table are pending until you decide
  5204. to keep or throw out the changes.  A call to trollback returns that
  5205. table back to its state prior to beginning the transaction.  tcommit
  5206. makes any changes during the transaction permanent.  In either case,
  5207. the transaction is considered over.  To start another transaction, you
  5208. must call the ttransact function again.  You can use the tmark to
  5209. determine whether a table is in a transaction or not.  tmark returns 1
  5210. if a table is in a transaction, and 0 if it is not.  If you close a table
  5211. which is in the middle of a transaction, the transaction is automatically
  5212. committed before the table is closed.  Similarly, if you drop a table
  5213. during a transaction, the transaction is automatically rolled back before
  5214. the table is dropped.
  5215.  
  5216. CRDE places few restrictions on what you can do with tables during a
  5217. transaction.  You can query and update them like any other table.  The
  5218. only limitation made by CRDE is that you may not create or drop secondary
  5219. indexes.  Attempting to do so will result in an error.
  5220.  
  5221. The following example demonstrates CRDE's transaction tracking
  5222. capabilities at work.
  5223.  
  5224.     include <conio,h>
  5225.     include <stdio.h>
  5226.     include "crde.h"
  5227.  
  5228.     int tbuffers = 256;     /* set as large as possible when doing
  5229.                 transaction tracking */
  5230.  
  5231.     int ShowTable(TABLE *t)
  5232.     {
  5233.       clrscr();
  5234.       gotoxy(1, 1); cputs("Displaying a table.");
  5235.       gotoxy(1, 2); cprintf("%ld rows found.", trows(t));
  5236.       return tview(t, NULL, NULL, 3, 3, 78, 24, 0x07, 0x07, 0x07, NULL);
  5237.     }
  5238.  
  5239.                      - 84 -
  5240.  
  5241.     int main()
  5242.     {
  5243.       TABLE *t;
  5244.  
  5245.       /* open and display table */
  5246.       t = topen("sups");
  5247.       ShowTable(t);
  5248.  
  5249.       /* begin transaction */
  5250.       ttransact(t);
  5251.  
  5252.       /* perform operation(s) on table */
  5253.       tdelete(t, "city", EQ, "Boston", NULL);
  5254.       ShowTable(t);
  5255.  
  5256.       /* restore original table with single function call! */
  5257.       trollback(t);
  5258.       ShowTable(t);
  5259.  
  5260.       tclose(t);
  5261.     }
  5262.     
  5263.  
  5264. How does transaction tracking work?  
  5265.  
  5266. To be able to rollback a table in a transaction, CRDE must record all
  5267. the changes you make to the table while the transaction is in effect.
  5268. CRDE keeps track of these changes in memory. This has several advantages,
  5269. the greatest of which is speed. This allows you to use transaction
  5270. tracking  with virtually no performance degradation.  The significant
  5271. drawback of this is that you are limited to the number of changes you
  5272. can make by the amount of available memory in your computer.  If the
  5273. tables you are performing transactions on are large and/or numerous,
  5274. you may run out of memory.  When this happens, CRDE functions will start
  5275. returning -11 "out of memory" error.  If memory runs out you should
  5276. immediately rollback any transactions before continuing.  The amount of
  5277. transactions which can be performed depends upon many factors:  the
  5278. number and size of table in the transaction, the number of changes
  5279. made, and available memory.  If the tables are small enough you will
  5280. be perform an unlimited amount of transactions.  To perform an
  5281. unlimited amount of transactions on a table, there should be at least
  5282.  
  5283.     size of table (on disk)  + size of indexes (on disk)
  5284.  
  5285. bytes of memory set aside in your buffers (see Chapter 2 about setting
  5286. the tbuffers system variable).  This is a rough estimate which does not
  5287. take into account other factors which could possibly make the requirements
  5288. steeper.  For optimum performance, you should consider setting the
  5289. number of buffers to double that amount.
  5290.  
  5291. Even large CRDE applications are no likely to be larger than 200-250k
  5292. (with CRDE linked).  On a 640k machine that would leave some 300-400k
  5293. available to CRDE.  With that much available memory, you should have
  5294. no problem performing unlimited transactions on a tables with up to
  5295. 5000 rows or more.
  5296.  
  5297.  
  5298.  
  5299.                      - 85 -
  5300.  
  5301.  
  5302.  
  5303.  
  5304.  
  5305.  
  5306. Chapter 15
  5307.  
  5308. Repairing Tables
  5309.  
  5310.  
  5311.  
  5312. Corrupted indexes  
  5313.  
  5314. The most common type of corruption is when an index becomes corrupted.
  5315. This can happen when CRDE is updating the indexes on a table and
  5316. something unexpected happens.  Index corruption will usually show itself
  5317. as a incorrect query result or a "disk seek error", meaning that pointers
  5318. are out of alignment in the index file.  If you can open a table with
  5319. topen, and you're having problems, this is probably what is wrong.
  5320. You may also have trouble viewing the table if its index(es) are corrupt.
  5321.  
  5322.  
  5323. Testing your indexes
  5324.  
  5325. There is an easy way to test the integrity of an index visually by
  5326. using the tview function.  Simply call tview on the table using the
  5327. same sort order as the index in question.  For example, suppose you
  5328. wanted to test the integrity of an index on the "city" column (if one
  5329. existed) in sups.  First you would set up tview to view the table like
  5330.  
  5331.     tview(sups, NULL, "city", 3, 3, 78, 23, 0x07, 0x07, 0x07, NULL);
  5332.  
  5333. Scroll throughout the entire table.  If tview aborts prematurely, the
  5334. cursor gets stuck, or negative numbers appear in the row number column,
  5335. the index probably corrupt.  You can rebuild any suspect indexes with
  5336. the tindex function.  Many times, however, more than one index is
  5337. corrupt.  An easier way to rebuild your indexes is to go to DOS, delete
  5338. them, and then open the table with topen.  topen will automatically
  5339. rebuild all of the missing indexes for you.  This is convenient when
  5340. you may not remember all of the indexes on a table.
  5341.  
  5342.  
  5343. Repairing a table with trepair
  5344.  
  5345. Sometimes, however, the problem is much deeper than a corrupt index.
  5346. For example, you may not even be able to open the table with topen,
  5347. or when you do, your whole program blows up.  If topen refuses to
  5348. open the table, your only alternative is to attempt to repair the table
  5349. itself.  Fortunately, CRDE provides such a repair facility.  trepair
  5350. is a CRDE function declared in crde.h as 
  5351.  
  5352.     TABLE *trepair(char *name, char *td);
  5353.  
  5354.  
  5355.  
  5356.  
  5357.  
  5358.  
  5359.                      - 86 -
  5360.  
  5361. name is the name of the table to be repaired.  td is a table descriptor
  5362. which must exactly describe the structure of the original table.
  5363. trepair uses this model to rebuild the table.  trepair will attempt to
  5364. recover as much of the table and primary index as it possibly can.
  5365. trepair does not repair secondary indexes.  However, once the table is
  5366. repaired, you can simply rebuild them with the tindex function.
  5367.  
  5368.  
  5369. Limitations of trepair
  5370.  
  5371. trepair can repair most logically damaged table without a hitch.
  5372. Logically damaged tables are those which contain incorrect or outdated
  5373. information.  Usually this type of corruption occurs when all unwritten
  5374. information was not committed to disk, such as during a power-outage
  5375. or similar catastrophe.  trepair can even fix certain types of
  5376. physically damaged tables.  Physically damaged tables are those which
  5377. have defective sectors or some other type of physical damage (the table
  5378. was partially overwritten or truncated).  However, if your table is
  5379. physically damaged, your problems may be deeper than just a corrupted
  5380. table.  Your disk may need to be reformatted or your disk drive repaired.
  5381. trepair however, is fairly limited in the amount of physical damage it
  5382. can repair.  trepair cannot unerase tables nor fix bad sectors or
  5383. clusters in a file.  In addition, trepair must be able to open a table
  5384. to repair it.
  5385.  
  5386.  
  5387.  
  5388.  
  5389.  
  5390.  
  5391.  
  5392.  
  5393.  
  5394.  
  5395.  
  5396.  
  5397.  
  5398.  
  5399.  
  5400.  
  5401.  
  5402.  
  5403.  
  5404.  
  5405.  
  5406.  
  5407.  
  5408.  
  5409.  
  5410.  
  5411.  
  5412.  
  5413.  
  5414.  
  5415.  
  5416.  
  5417.  
  5418.  
  5419.                      - 87 -
  5420.  
  5421.  
  5422.  
  5423.  
  5424.  
  5425.  
  5426. Reference Manual
  5427.  
  5428.  
  5429.  
  5430. This chapter contains a detailed description of all the functions in
  5431. the CRDE library.
  5432.  
  5433. The following sample library look-up entry explains how to use this
  5434. portion of the Referance Manual to reference CRDE library functions.
  5435.  
  5436.  
  5437. Name            The function's name.
  5438.  
  5439. Description      Simple explanation of what the function does.
  5440.  
  5441. Declaration      Function's full standard C declaration.
  5442.  
  5443. Remarks         A detailed explantion of how a function works, how to
  5444.         use it, what its used for, and any other details
  5445.         relating to the function.
  5446.  
  5447. Return Value    Possible return values for the function.
  5448.  
  5449. See Also        Related functions/topics.
  5450.  
  5451. Example          An example demonstrating how to use the function.
  5452.  
  5453. Output           Output of the example program, if any.
  5454.  
  5455.  
  5456. Standard conventions in CRDE
  5457.  
  5458. The CRDE function library was designed to be consistant in the way
  5459. that functions return values and accept parameters.   Below are some
  5460. basic rules common to all CRDE functions.
  5461.  
  5462.  
  5463. Return values
  5464.  
  5465. Every CRDE function returns some type of error indicator in the event
  5466. of an error.  The type of indicator depends upon the return type of
  5467. the function.  
  5468.  
  5469.  
  5470.  
  5471.  
  5472.  
  5473.  
  5474.  
  5475.  
  5476.  
  5477.  
  5478.  
  5479.                      - 88 -
  5480.  
  5481. Return type        Possible return values    
  5482.  
  5483. TABLE *         Returns a pointer to an open table if successful, or
  5484.         NULL if an error occurs.
  5485.  
  5486. int         Returns 0 if successful, or a negative error code if
  5487.         an error occurs.
  5488.  
  5489. long              Returns the number of rows involved in the operation,
  5490.         or a negative error code if an error occurs.
  5491.  
  5492. char              Returns 0 on error.
  5493.  
  5494.  
  5495. Note:  All of CRDE's error codes are negative integers.  In addition,
  5496. CRDE sets the global variable terrno to the appropriate error code
  5497. whenever an error occurs.  If the function does not return an integer
  5498. type, you can use terrno to determine the cause of the error.  If the
  5499. function does return an integer type then terrno will match the error
  5500. code returned by the function.  CRDE also has another global variable
  5501. called trowsfound.  trowsfound contains the number of rows (if any)
  5502. involved in the last operation.  Like terrno, trowsfound can be used to
  5503. determine the number of rows affected by the last operation if the
  5504. function does not return an integer value.  In the event of an error,
  5505. trowsfound will hold the number of rows affected before the error occured.
  5506.  
  5507.  
  5508. Functions which return a TABLE *
  5509.  
  5510. Every CRDE function which returns a TABLE * accepts a char *name as
  5511. its first parameter.  name may be any of three values:
  5512.  
  5513.     TEMP        The table is a TEMP table.  
  5514.     STATIC       The table is a STATIC table.
  5515.  
  5516. If a table name is not TEMP or STATIC, it is assumed to be an asciiz
  5517. string containing the table's base name.  The string maybe any legal
  5518. DOS pathname up to 64 characters in length.  It may not include
  5519. wildcards or an extension.  Any table previously existing by that name
  5520. is automatically overwritten by the new table.  If a table is TEMP or
  5521. STATIC, it is known as a temporary table.  Otherwise the table is a
  5522. permanent table.  
  5523.  
  5524. Some functions return a table whose structure is identical to the one
  5525. passed to it as a parameter.  Some examples are tcopy, tborrow, and
  5526. tselect.  What this means is that the resulting table will have the
  5527. same column names, types, and primary key as the original table.
  5528. Secondary indexes,  however, are not considered as part of a table's
  5529. structure and are never inherited by the result table.
  5530.  
  5531. Several functions which accept two tables as parameters require that
  5532. both tables be compatible.  Examples are tunion, tdiff, and tintersect.
  5533. Two tables are compatible if they have identical column types and sizes
  5534. on a column by column basis.  They may have different column names, and/
  5535. or a different primary key and secondary keys.
  5536.  
  5537.  
  5538.  
  5539.                      - 89 -
  5540.  
  5541. In some functions, whether or not a table has a primary key can have
  5542. an affect on how the function works.  Such variations are noted in the
  5543. reference guide.
  5544.  
  5545.  
  5546. Parameters in CRDE functions
  5547.  
  5548. When at all possible (in the Reference Manual and in crde.h), function
  5549. prototypes use symbolic parameter names to help identify the meaning of
  5550. the parameter.  Those parameters and their meanings are listed below.
  5551.  
  5552. Paramter name    Meaning
  5553.  
  5554. name        The name parameter is the first parameter of any
  5555.         CRDE function returning a TABLE *.  name may be
  5556.         TEMP, STATIC, or a pointer to a legal DOS
  5557.         pathname in asciiz format.  The pathname may not
  5558.         contain wildcards or an extension.
  5559.  
  5560. t, t1, t2, etc.    These parameters are tables passed to the function.  
  5561.  
  5562. c        This parameter expects a pointer to an asciiz string
  5563.         containing a column name  like "cust#".
  5564.  
  5565. cl        This parameter expects a pointer to a asciiz string
  5566.         containing a list of column names seperated by
  5567.         whitespace.
  5568.  
  5569. td        This parameter expects a table descriptor which is
  5570.         very similar to the column list above.  A table
  5571.         descriptor also contains data type information.  Any
  5572.         column preceded with an asterisk '*' are part of the
  5573.         table's primary key.
  5574.  
  5575. id        This parameter expects an index descriptor similar to
  5576.         the column list above.  However, any columns
  5577.         preceded with and exclaimation mark '!' are indexed in
  5578.         descending order.
  5579.  
  5580. pl        This parameter expects a special type of column list. 
  5581.         Columns in the list may be preceded with an asterisk
  5582.         '*', marking them as part of a primary key.  It is
  5583.         different from a table descriptor because no data type
  5584.         information is required.
  5585.  
  5586.  
  5587. Auto-cleanup of TEMP tables
  5588.  
  5589. When a TEMP table is passed to a CRDE function, it may be automatically
  5590. dropped when the function completes.  This feature allows you to nest
  5591. functions which return tables inside one another safely.  The general
  5592. rule is:  
  5593.  
  5594.  
  5595.  
  5596.  
  5597.  
  5598.  
  5599.                      - 90 -
  5600.  
  5601.     A CRDE function will automatically drop a TEMP table
  5602.     passed to  it as a parameter if the function does not or can not
  5603.     alter the table in any way. If the TEMP table cannot be thus
  5604.     affected by the function, it will be automatically dropped when
  5605.     the function  completes, even in the event of an error.
  5606.  
  5607. Some CRDE functions will not automatically drop a table, regardless of
  5608. its type. These functions are:  ttransact, tcommit, trollback, tmark,
  5609. twritec, tnormal, tmode.
  5610.  
  5611.  
  5612.  
  5613.  
  5614.  
  5615.  
  5616.  
  5617.  
  5618.  
  5619.  
  5620.  
  5621.  
  5622.  
  5623.  
  5624.  
  5625.  
  5626.  
  5627.  
  5628.  
  5629.  
  5630.  
  5631.  
  5632.  
  5633.  
  5634.  
  5635.  
  5636.  
  5637.  
  5638.  
  5639.  
  5640.  
  5641.  
  5642.  
  5643.  
  5644.  
  5645.  
  5646.  
  5647.  
  5648.  
  5649.  
  5650.  
  5651.  
  5652.  
  5653.  
  5654.  
  5655.  
  5656.  
  5657.  
  5658.  
  5659.                      - 91 -
  5660.  
  5661. ----------------------------------------------------------------
  5662. day
  5663. ----------------------------------------------------------------
  5664.  
  5665. Description    Determines calender day of a date_t value.
  5666.  
  5667. Declaration    int day(date_t d);
  5668.  
  5669. Remarks        day returns a value between 1 and 31.
  5670.  
  5671. Return Value    day returns the calender day of the specified date,
  5672.         or -1 if the date is invalid.
  5673.  
  5674. See Also    month, year
  5675.  
  5676. Example        #include "date.h"
  5677.  
  5678.         int printdate(date_t d);
  5679.         {
  5680.           printf("%d/%d/%d",  month(d), day(d), year(d));
  5681.         }
  5682.  
  5683.  
  5684.  
  5685. ----------------------------------------------------------------
  5686. dayofweek
  5687. ----------------------------------------------------------------
  5688.  
  5689. Description    Determines the day of week of a particular date.
  5690.  
  5691. Declaration    int dayofweek(date_t d);
  5692.  
  5693. Remarks        dayofweek returns a value from 0 to 6 correponding the
  5694.         day of week, Sunday to Saturday, of the specified date.
  5695.         date.h provides 7 constants analogous to the return
  5696.         value of the dayofweek function.
  5697.  
  5698.         #define SUNDAY        0
  5699.         #define MONDAY        1
  5700.         etc...
  5701.         #define SATURDAY    6 
  5702.  
  5703. Return Value    dayofweek  returns a value from 0 to 6, corresponding
  5704.         to the day of week, or -1 if the date is invalid.
  5705.  
  5706. See Also    firstday.
  5707.  
  5708. Example        #include "date.h"
  5709.  
  5710.         /* functions to determine whether a date is on a weekend
  5711.         or weekday */
  5712.  
  5713.  
  5714.  
  5715.  
  5716.  
  5717.  
  5718.  
  5719.                      - 92 -
  5720.  
  5721.         int weekend(date_t d)
  5722.         {
  5723.           int i;
  5724.  
  5725.           i = dayofweek(d);
  5726.           return i == SATURDAY || i == SUNDAY;
  5727.         }
  5728.  
  5729.         int weekday(date_t d)
  5730.         {
  5731.           return !weekend(d);
  5732.         }
  5733.     
  5734.  
  5735.  
  5736. ----------------------------------------------------------------
  5737. dayofyear
  5738. ----------------------------------------------------------------
  5739.  
  5740. Description    Determines the day of year of some calender date.
  5741.  
  5742. Declaration    int dayofyear(int month, int day, int year);
  5743.  
  5744. Remarks        dayofyear returns a value between 1 and 366 where
  5745.         January 1 is day 1, January 2 is day 2, February 1 is
  5746.         day 32, etc.
  5747.  
  5748. Return Value    Returns the day of year, or -1 if the date is invalid.
  5749.  
  5750. See Also    monthday.
  5751.  
  5752. Example        #include <stdio.h>
  5753.         #include "date.h"
  5754.  
  5755.         int main()
  5756.         {
  5757.           char buf[16];
  5758.           int m, d, y;
  5759.  
  5760.           printf("Enter date: ");
  5761.           scanf(" %d / %d / %d", &m, &d, &y);
  5762.  
  5763.           printf("Days since Jan 1 is %d.\n", dayofyear(m, d, y));
  5764.         }
  5765.  
  5766.  
  5767. Output        Enter date: 2/1/1990
  5768.         Days since Jan 1 is 32.
  5769.  
  5770.  
  5771.  
  5772.  
  5773.  
  5774.  
  5775.  
  5776.  
  5777.  
  5778.  
  5779.                      - 93 -
  5780.  
  5781. ----------------------------------------------------------------
  5782. daysinyear
  5783. ----------------------------------------------------------------
  5784.  
  5785. Description    Returns number of days in a year.
  5786.  
  5787. Declaration    int daysinyear(int year);
  5788.  
  5789. Remarks        daysinyear returns value of 365 or 366 depending upon
  5790.         whether the year is a leap year or not.
  5791.  
  5792. Return Value    daysinyear returns the number of days in a year.
  5793.  
  5794. See Also    leapyear.
  5795.  
  5796. Example        #include <stdio.h>
  5797.         #include "date.h"
  5798.  
  5799.         int main()
  5800.         {
  5801.           printf("Days in the year 1990 = %d.\n",
  5802.           daysinyear(1990));
  5803.  
  5804.           printf("Days in the year 2000 = %d.\n",
  5805.           daysinyear(2000));
  5806.         }
  5807.  
  5808. Output        Days in the year 1990 = 365.
  5809.         Days in the year 2000 = 366.
  5810.  
  5811.  
  5812.  
  5813. ----------------------------------------------------------------
  5814. firstday
  5815. ----------------------------------------------------------------
  5816.  
  5817. Description    Returns date of first day of week in a particular month.
  5818.  
  5819. Declaration    date_t firstday(int month, int year, int dayofweek);
  5820.  
  5821. Remarks        firstday returns a date, as a date_t value, for the
  5822.         first dayofweek in that month and year.  For example,
  5823.         to get the date of the first Monday of August 1990
  5824.         would look like
  5825.  
  5826.             date_t firstmon;
  5827.  
  5828.             firstmon = firstday(6, 1990, MONDAY);
  5829.  
  5830.         MONDAY is a constant defined in date.h specifying the
  5831.         day of the week.
  5832.  
  5833.  
  5834.  
  5835.  
  5836.  
  5837.  
  5838.  
  5839.                      - 94 -
  5840.  
  5841.         firstday can also be used to calculate the second,
  5842.         third, and fourth occurances of a date by simply adding
  5843.         the appropriate multiple of 7 to the result.  The
  5844.         second Monday in June 1990 would be
  5845.  
  5846.             firstday(6, 1990, MON) + 7;
  5847.  
  5848. Return Value    firstday returns a date_t value or -1 if the date is
  5849.         invalid.
  5850.  
  5851. See Also    dayofweek.
  5852.  
  5853. Example        /*  function to determine if today is a payday (the
  5854.         second and fourth Fridays of every month). */
  5855.  
  5856.         int PayDay(date_t today)
  5857.         {
  5858.           int m, y;
  5859.           date_t d;
  5860.  
  5861.           m = month(today); y = year(today);
  5862.           d = firstday(m, y, FRIDAY);
  5863.  
  5864.           return (today == d+7) || (today == d+21);
  5865.         }
  5866.  
  5867.  
  5868.  
  5869. ----------------------------------------------------------------
  5870. gmdate
  5871. ----------------------------------------------------------------
  5872.  
  5873. Description    Converts a date_t value into a calender date.
  5874.  
  5875. Declaration    int gmdate(date_t d, int *month, int *day, int *year);
  5876.  
  5877. Remarks        gmdate converts the date_t value d into its calender
  5878.         definition, and stores the results in month, day, and
  5879.         year.
  5880.  
  5881. Return Value    gmdate returns 0 if successful, or -1 if the date was
  5882.         invalid.
  5883.  
  5884. See Also    mkdate.
  5885.  
  5886. Example        /* function to return the first day of the month of the
  5887.         current date */
  5888.  
  5889.         date_t FirstOfMonth(date_t dt)
  5890.         {
  5891.           int m, d, y;
  5892.  
  5893.           gmdate(dt, &m, &d, &y);
  5894.           return mkdate(m, 1, y);
  5895.         }
  5896.  
  5897.  
  5898.  
  5899.                      - 95 -
  5900.  
  5901. ----------------------------------------------------------------
  5902. leapyear
  5903. ----------------------------------------------------------------
  5904.  
  5905. Description    Determines if a year is a leap year.
  5906.  
  5907. Declaration    int leapyear(int year);
  5908.  
  5909. Remarks        leapyear recognizes both leap years and leap centuries.
  5910.         A year is a leap year is it is evenly divisible by 4,
  5911.         excluding those centuries (1800, 1900, 2100, etc.) not
  5912.         evenly divisible by 400.
  5913.  
  5914. Return Value    leapyear returns 1 year is a leap year, or 0 if it is not.
  5915.  
  5916. See Also    daysinyear.
  5917.  
  5918. Example        #include <stdio.h>
  5919.         #include "date.h"
  5920.  
  5921.         int main()
  5922.         {
  5923.           int i;
  5924.  
  5925.           printf("Enter year: ");
  5926.           scanf(" %d", &i);
  5927.           if (leapyear(i))
  5928.             printf("%d is a leap year.\n", i);
  5929.           else
  5930.             printf(%d is not a leap year.\n", i);
  5931.         }
  5932.  
  5933.  
  5934.  
  5935. ----------------------------------------------------------------
  5936. mkdate
  5937. ----------------------------------------------------------------
  5938.  
  5939. Description    Converts a calender date to a date_t value.
  5940.  
  5941. Declaration    date_t mkdate(int month, int day, int year);
  5942.  
  5943. Remarks        mkdate converts the calender date month, day, year
  5944.         into its date_t equivelent.  mkdate knows about the
  5945.         days in    months and years, and about leap years, and
  5946.         will return -1 if the date is invalid, for example
  5947.         2/29/1990.
  5948.  
  5949.         mkdate can convert dates from 1/1/1 to 12/31/32767.
  5950.  
  5951.         A common error when using mkdate is to forget to
  5952.         indicate the correct century in the year, for exmaple
  5953.  
  5954.             mkdate(4, 17, 90)
  5955.  
  5956.         specifies the date 4/17/0090 not 4/17/1990.
  5957.  
  5958.  
  5959.                      - 96 -
  5960.  
  5961. Return Value    mkdate returns a date_t value or -1 if the date is
  5962.         invalid.
  5963.  
  5964. See Also    gmdate.
  5965.  
  5966. Example        #include <stdio.h>
  5967.         #include "date.h"
  5968.  
  5969.         int main()
  5970.         {
  5971.           date_t today;
  5972.           int m, d, y;
  5973.  
  5974.           do {
  5975.             printf("Enter today's date: ");
  5976.             scanf(" %d / %d / %d", &m, &d, &y);
  5977.             today = mkdate(m, d, y);
  5978.           } while (today < 0);
  5979.  
  5980.           gmdate(today + 7, &m, &d, &y);
  5981.           printf("Date a week from now = %d/%d/%d.\n", m, d, y);
  5982.         }
  5983.  
  5984.  
  5985.  
  5986. ----------------------------------------------------------------
  5987. month
  5988. ----------------------------------------------------------------
  5989.  
  5990. Description    Returns calender month of a date_t value.
  5991.  
  5992. Declaration    int month(date_t d);
  5993.  
  5994. Remarks        month returns a value between 1 and 12.
  5995.  
  5996. Return Value    month returns the month of the date or -1 if the
  5997.         date is invalid.
  5998.  
  5999. See Also    day, year.
  6000.  
  6001. Example        See day.
  6002.  
  6003.  
  6004.  
  6005. ----------------------------------------------------------------
  6006. monthadd
  6007. ----------------------------------------------------------------
  6008.  
  6009. Description    Performs month addition/subtraction on a date.
  6010.  
  6011. Declaration    date_t monthadd(date_t d, int months);
  6012.  
  6013.  
  6014.  
  6015.  
  6016.  
  6017.  
  6018.  
  6019.                      - 97 -
  6020.  
  6021. Remarks        monthadd adds the specified number of months to a date
  6022.         creating a new date.  monthadd knows about calender
  6023.         dates and leap years, and correctly adds the number of
  6024.         months specified.  For example
  6025.  
  6026.             monthadd(mkdate(2, 1, 1992), 1);
  6027.  
  6028.         correctly yields 3/1/1992.
  6029.  
  6030.         You can subtract months from a date by simply specifying
  6031.         a negative value for months.
  6032.  
  6033. Return Value    monthadd returns a date_t value, or -1 if the date is
  6034.         invalid.
  6035.  
  6036. See Also    yearadd.
  6037.  
  6038. Example         #include "date.h"
  6039.  
  6040.         /* function to determine if an payment was received
  6041.         within a month of its due date.  If not, a surcharge
  6042.         will be added to the amount owed */
  6043.  
  6044.         int ReturnBy(date_t due, date_t received)
  6045.         {
  6046.           return received <= monthadd(due, 1);
  6047.         }
  6048.  
  6049.  
  6050.  
  6051. ----------------------------------------------------------------
  6052. taccess
  6053. ----------------------------------------------------------------
  6054.  
  6055. Description    Determines the accessibility of a table.
  6056.  
  6057. Declaration    int taccess(char *name, int mode);
  6058.  
  6059. Remarks        taccess tests a file for certain accessibility rights.
  6060.         mode determines the type of test which will be done.
  6061.         When mode is 0 taccess tests for existance of a file.
  6062.         For example, to see if the "sups" table had been created
  6063.  
  6064.             TABLE *sups;
  6065.  
  6066.             if (taccess("sups", 0) == 0)
  6067.               /* file exists so open it */
  6068.               sups = topen("sups");
  6069.             else
  6070.               /* file does not exist, create new */
  6071.               sups = tcreat("sups", "*s# i, sname c26, city"
  6072.                 "c26, status i");
  6073.  
  6074.         taccess returns 0 if the tables exists.  You can test
  6075.         for write permission to a table by setting mode to 2.
  6076.  
  6077.  
  6078.  
  6079.                      - 98 -
  6080.  
  6081. Return Value    taccess returns 0 if access rights tested positive, or
  6082.         -1 if not.
  6083.  
  6084.  
  6085.  
  6086. ----------------------------------------------------------------
  6087. tadd
  6088. ----------------------------------------------------------------
  6089.  
  6090. Description      Inserts records from one table into another.
  6091.  
  6092. Declaration      long tadd(TABLE *t1, TABLE *t2);
  6093.  
  6094. Remarks         tadd inserts the records in t2 into t1.  If t1 is
  6095.         keyed, only those records which do not violate the
  6096.         primary key will be inserted.
  6097.  
  6098.         If t2 is a TEMP table, t2 is automatically dropped
  6099.         by tadd, even if an error occurs.  Otherwise, t2 is
  6100.         unaffected by tadd.
  6101.  
  6102. Return Value     tadd returns the number of records added if successful,
  6103.         or an error code if an error occured.
  6104.  
  6105. See Also         tupdate, tsubtract.
  6106.  
  6107. Example        /* function to combine orders made during a month to a
  6108.         history file containing all previous orders.  Orders
  6109.         table and History table are assumed to have compatible
  6110.         structures. */
  6111.  
  6112.         int SaveOrders(TABLE *orders, TABLE *history)
  6113.         {
  6114.           long i;
  6115.  
  6116.           i = tadd(history, orders);
  6117.           if (i > 0)
  6118.             /* if orders successfully added then clear
  6119.             orders table for next month */
  6120.             tempty(orders);
  6121.           return i > 0 ? 0 : i;
  6122.         }
  6123.  
  6124.  
  6125.  
  6126. ----------------------------------------------------------------
  6127. tall
  6128. ----------------------------------------------------------------
  6129.  
  6130. Description      Performs a (user-defined) function on all open tables.
  6131.  
  6132. Declaration     int tall(int (*func)(TABLE *));
  6133.  
  6134.  
  6135.  
  6136.  
  6137.  
  6138.  
  6139.                      - 99 -
  6140.  
  6141. Remarks         tall performs a function, accepting a TABLE * as a
  6142.         parameter, returning an int, on all currently open
  6143.         tables (temporary and permanent).  CRDE keeps an
  6144.         active list of all open tables during the course of
  6145.         a program's execution.  tall simply traverses this
  6146.         list, performing func on each table.  tall is designed
  6147.         to work even with functions which alter the table list
  6148.         such as topen and tclose.  tall will only perform func
  6149.         on every table which was open immediately prior to
  6150.         calling tall.  This allows tall to work with functions
  6151.         like tclose, and also prevents it from working
  6152.         recursively on functions which open tables.
  6153.  
  6154.         tall will work directly with many CRDE functions such
  6155.         as:  tclose, tdrop, tempty, ttransact, trollback,
  6156.         tcommit, twritec, tnormal, tflush, and trelease.
  6157.  
  6158. Return Value     tall returns the first non-zero result of func,
  6159.         otherwise it returns 0.
  6160.  
  6161. Example #1         #include <stdio.h>
  6162.               #include "crde.h"
  6163.  
  6164.         int tbuffers = 128;
  6165.  
  6166.               int main()
  6167.               {
  6168.           TABLE *sups, *parts, *orders;
  6169.  
  6170.           /* open some tables */
  6171.           sups = topen("sups");
  6172.           parts = topen("parts");
  6173.           orders  = topen("orders");
  6174.  
  6175.           /* put all in writec mode */
  6176.           tall(twritec);
  6177.  
  6178.           /*
  6179.             rest of user-program here...
  6180.           */
  6181.  
  6182.           /* closes all tables */
  6183.           tall(tclose);
  6184.         }
  6185.  
  6186. Example #2       #include <stdio.h>
  6187.         #include "crde.h"
  6188.  
  6189.         /* function which closes all temporary tables */
  6190.  
  6191.  
  6192.  
  6193.  
  6194.  
  6195.  
  6196.  
  6197.  
  6198.  
  6199.                     - 100 -
  6200.  
  6201.               int tdroptemp(TABLE *t);
  6202.               {
  6203.           int i;
  6204.  
  6205.           i = tmode(t);
  6206.           return (i == T_TEMP || i == T_STATIC) ? tdrop(t) : 0;
  6207.         }
  6208.  
  6209.               int tdroptempall()
  6210.               {
  6211.           return tall(tdroptemp);
  6212.         }
  6213.  
  6214.  
  6215.  
  6216. ----------------------------------------------------------------
  6217. taverage
  6218. ----------------------------------------------------------------
  6219.  
  6220. Description      Calculates the average of a column.
  6221.  
  6222. Declaration      int taverage(TABLE *t, char *c, void *result);
  6223.  
  6224. Remarks          taverage calculates the average of a column in a
  6225.         table and places the result in the buffer pointed
  6226.         to by result.  c must be acolumn in the table.
  6227.  
  6228.         taverage only works with types i, l, f, and $.  The
  6229.         type of buffer pointed to by  result is always double
  6230.         for taverage, regardless of the column type.
  6231.  
  6232.         The value in result is undefined if an error occurs.
  6233.  
  6234.         If t is a TEMP table, it will automatically be dropped
  6235.         by taverage, even if an error occurs.  Otherwise, t is
  6236.         unaffected by taverage.
  6237.  
  6238. Return Value     taverage returns 0 in successful, or an error code if
  6239.         an error occured.
  6240.  
  6241. See Also         tcount, tmax, tmin, tsum.
  6242.  
  6243. Example          #include <stdio.h>
  6244.               #include "crde.h"
  6245.  
  6246.         int tbuffers = 128;
  6247.  
  6248.               int main()
  6249.               {
  6250.           TABLE *sups, *answer;
  6251.           double avg;
  6252.  
  6253.           sups = topen("sups");
  6254.  
  6255.  
  6256.  
  6257.  
  6258.  
  6259.                     - 101 -
  6260.  
  6261.           /* query all suppliers with above average status */
  6262.  
  6263.           taverage(sups, "status", &avg);
  6264.           answer = tselect(TEMP, sups, "status", GT, (int)avg,
  6265.             NULL);
  6266.  
  6267.           tview(answer, NULL, NULL, 3,3, 78, 23, 0x07, 0x07,
  6268.             0x07, NULL);
  6269.  
  6270.           tall(tclose);
  6271.         }
  6272.  
  6273.  
  6274.  
  6275. ----------------------------------------------------------------
  6276. tborrow
  6277. ----------------------------------------------------------------
  6278.  
  6279. Description     Creates a new (empty) table like another table.
  6280.  
  6281. Declaration      TABLE *tborrow(char *name, TABLE *t);
  6282.  
  6283. Remarks          tborrow creates a new (empty) table with the exact
  6284.         same structure t.  The result table will have same
  6285.         columns and primary key as t.  However, any secondary
  6286.         keys are not copied by tborrow.
  6287.  
  6288.         If t is a TEMP table, it is automatically dropped by
  6289.         tborrow, even if an error occurs.  Otherwise, t is
  6290.         unaffected by tborrow.
  6291.  
  6292. Return Value     tborrow returns a pointer to an open table if
  6293.         successful, or NULL if an error occured.
  6294.  
  6295. See Also         tcopy.
  6296.  
  6297. Example        #include <stdio.h>
  6298.         #include "crde.h"
  6299.  
  6300.         int tbuffers = 128;
  6301.  
  6302.         int main()
  6303.         {
  6304.           TABLE *sups, *saved;
  6305.  
  6306.           /* program to remove some suppliers from the Sups
  6307.           table and place them in a special achive table named
  6308.           Saved */
  6309.  
  6310.           sups = topen("sups");
  6311.           saved = tborrow("saved", sups);
  6312.  
  6313.           tadd(saved, tselect(TEMP, sups, "status", LT, 5, NULL);
  6314.           tdelete(sups, "status", LT, 5, NULL);
  6315.  
  6316.  
  6317.  
  6318.  
  6319.                     - 102 -
  6320.  
  6321.           tadd(saved, tselect(TEMP, sups, "status", GT, 25, NULL);
  6322.           tdelete(sups, "status", GT 25, NULL);
  6323.  
  6324.           tclose(sups);
  6325.           tclose(saved);
  6326.         }
  6327.  
  6328.             
  6329.  
  6330. ----------------------------------------------------------------
  6331. tchange
  6332. ----------------------------------------------------------------
  6333.  
  6334. Description      Changes rows in a table.
  6335.  
  6336. Declaration      long tchange(TABLE *t, ...);
  6337.  
  6338. Remarks         tchange accepts two NULL terminated argument lists,
  6339.         the first, a list of the changes to be made, followed
  6340.         by a search expression to determine which of the
  6341.         records to change.
  6342.  
  6343.         A change list consists of zero or more changes.  A
  6344.         change consists of a column name followed by a value.
  6345.  
  6346.         A typical tchange call will have the format:
  6347.  
  6348.         tchange(t, chcol1, chval1, chcol2, chval2, ..., NULL,
  6349.           colname1, relop1, value1, ..., NULL);
  6350.  
  6351.         chcol[n] can be any valid column name of t.
  6352.  
  6353.         chval[n] may be either a constant or a variable.  The
  6354.         type of chval[n] must be exactly the type of chcol[n].
  6355.         The value of chcol[n] is changed to chval[n] in every
  6356.         record selected.
  6357.  
  6358.         The search expression is identical to the one described
  6359.         in tselect.  Only records which match the search
  6360.         expression are changed.
  6361.  
  6362.               Here are some examples:
  6363.  
  6364.               /* set all quantities in a orders table to 0 */
  6365.               tchange(orders, "qty", 0, NULL, NULL);
  6366.  
  6367.               /* change credit of a single customer */
  6368.               int newquant, custno;
  6369.               ...
  6370.         tchange(cust, "qty", newquant, NULL, "cust#", EQ, custno,
  6371.           NULL);
  6372.  
  6373.         tchange accepts a maximum of 16 changes.  It is not an
  6374.         error to specify no changes, but doing so will have no
  6375.         effect on the table.
  6376.  
  6377.  
  6378.  
  6379.                     - 103 -
  6380.  
  6381. Return Value     tchange returns the number of rows changed if
  6382.         successful, or an error code if an error occurred.
  6383.  
  6384. See Also         tchangeif, tselect.
  6385.  
  6386. Example     /* routine to change a supplier id # while maintaining
  6387.         referential integrity */
  6388.  
  6389.         int ChangeSup(int oldsup, int newsup)
  6390.         {
  6391.           int i;
  6392.  
  6393.           i = tchange(sups, "s#", newsup, NULL, "s#", EQ,
  6394.             oldsup, NULL);
  6395.           if (i > 0)
  6396.             i = tchange(orders, "s#", newsup, NULL, "s#", EQ,
  6397.               oldsup, NULL);
  6398.           return i;
  6399.         }
  6400.  
  6401.  
  6402.  
  6403. ----------------------------------------------------------------
  6404. tchangeif
  6405. ----------------------------------------------------------------
  6406.  
  6407. Description        Changes rows in a table.
  6408.  
  6409. Declaration      long tchangeif(TABLE *t, int (*action)(void *), ...);
  6410.  
  6411. Remarks          tchangeif is exactly like tchange except it accepts an
  6412.         additional parameter action.  Every row which matches
  6413.         the search expression is passed to action for further
  6414.         evaluation.  action should behave exactly as the action
  6415.         described in tselectif:
  6416.  
  6417.               Result of action     What happens
  6418.  
  6419.             > 0        Row is changed.
  6420.  
  6421.                  = 0              Row is not changed.
  6422.  
  6423.             < 0              An error occurred.  tchangeif
  6424.                     is aborted and returns the
  6425.                     result of action.
  6426.  
  6427.               See tselectif for more details on action.
  6428.           
  6429.         tchangeif differs from the other "..if functions" in
  6430.         one small detail.  Any changes to the row passed to
  6431.         action are made permanent along with any changes
  6432.         listed in the change list (if, of course, action
  6433.         returns an integer > 0.)  Note:  any changes listed
  6434.         in the change list are made after the call to action,
  6435.         and so will override any changes made inside of action
  6436.         itself.
  6437.  
  6438.  
  6439.                     - 104 -
  6440.  
  6441.         For example to give everyone in an employee table a 5%
  6442.         salary increase:
  6443.  
  6444.         TABLE *emps;
  6445.  
  6446.               int increase(void *rec)
  6447.               {
  6448.           /* user-defined if function - note that it is
  6449.           necessary to cast void * rec. */
  6450.  
  6451.           ((emp *)rec)->salary = ((emp *)rec)->salary * 0.05;
  6452.  
  6453.           return 1;
  6454.         }
  6455.         ...
  6456.         tchangeif(emps, increase, NULL, NULL);
  6457.  
  6458. Return Value     tchangeif returns the number of records changed if
  6459.         successful, or an error code if an error occurred.
  6460.  
  6461. See Also         tchange, tselect, tselectif.
  6462.  
  6463.  
  6464.  
  6465. ----------------------------------------------------------------
  6466. tchcol
  6467. ----------------------------------------------------------------
  6468.  
  6469. Description     Renames a column.
  6470.  
  6471. Declaration      int tchcol(TABLE *t, char *old, char *new);
  6472.  
  6473. Remarks          tchcol renames column old as new.  old must be a valid
  6474.         column name in t, and new must be an unique to t.  The
  6475.         position, datatype, and primary key of the column
  6476.         remain the same.
  6477.  
  6478.         A transaction keeps track of any columns which are
  6479.         renamed, so rolling back a transaction will cause any
  6480.         columns which were renamed during the transaction to
  6481.         be reversed.
  6482.  
  6483.         You may not use tchcol on a table which is in read-
  6484.         only mode.  See tselectif for details.
  6485.  
  6486. Return Value     tchcol returns 0 if successful, or an error code if an
  6487.         error occurred.
  6488.  
  6489. Example        #include "crde.h"
  6490.  
  6491.         int tbuffers = 128;
  6492.  
  6493.  
  6494.  
  6495.  
  6496.  
  6497.  
  6498.  
  6499.                     - 105 -
  6500.  
  6501.         int main()
  6502.         {
  6503.           TABLE *t;
  6504.  
  6505.           /* program to change the sups column "sname" to "name" */
  6506.  
  6507.           t = topen("sups");
  6508.           tchcol(t, "sname", "name");
  6509.           tclose(t);
  6510.         }
  6511.  
  6512.  
  6513.  
  6514. ----------------------------------------------------------------
  6515. tclose
  6516. ----------------------------------------------------------------
  6517.  
  6518. Description      Closes a table.
  6519.  
  6520. Declaration      int tclose(TABLE *t);
  6521.  
  6522. Remarks          tclose closes a table and all its associated indexes.
  6523.         Unless the table is a temporary table, all unwritten
  6524.         buffers are flushed to disk before closing.  Likewise,
  6525.         any transaction pending on the table is automatically
  6526.         commited before closing.  All memory held by table is
  6527.         released upon closing.  If the table is a temporary table
  6528.         (TEMP or STATIC) it is deleted upon closing.  There is
  6529.         no difference between closing and dropping a temporary
  6530.         table.
  6531.  
  6532.         A program should always close all tables before
  6533.         completing.  A simple way to insure this is to issue a
  6534.  
  6535.             tall(tclose);
  6536.  
  6537.               at the end of a program.
  6538.  
  6539.         tclose closes a table even if an error occurs.
  6540.  
  6541. Return Value     tclose returns 0 on success, or an error code if an error
  6542.         occured.
  6543.  
  6544. See Also         tdrop, topen.
  6545.  
  6546. Example        #include "crde.h"
  6547.  
  6548.         int tbuffers = 128;
  6549.  
  6550.         int main()
  6551.         {
  6552.           TABLE *sups, *parts, *orders;
  6553.  
  6554.           sups = topen("sups");
  6555.           parts = topen("parts");
  6556.           orders = topen("orders");
  6557.  
  6558.  
  6559.                     - 106 -
  6560.  
  6561.           /* main part of program here... */
  6562.  
  6563.           tclose(sups);
  6564.           tclose(parts);
  6565.           tclose(orders);
  6566.         }
  6567.  
  6568.  
  6569.  
  6570. ----------------------------------------------------------------
  6571. tcmpstruct
  6572. ----------------------------------------------------------------
  6573.  
  6574. Description      Compares two table's structures.
  6575.  
  6576. Declaration      int tcmpstruct(TABLE *t1, TABLE *t2);
  6577.  
  6578. Remarks          tcmpstruct compares the tables on a column by column
  6579.         basis, for identical types and sizes and primary key.
  6580.         Column names are not considered.  tcmpstruct returns
  6581.         one of the following:
  6582.  
  6583.               Return value        Meaning
  6584.  
  6585.             0        Tables are different.
  6586.  
  6587.                1            Tables have same structures but
  6588.                     different primary keys.
  6589.  
  6590.                2            Tables are identical.
  6591.  
  6592.         Whether or not either table has any secondary keys is
  6593.         not considered by tcmpstruct.
  6594.  
  6595. Return Value     tcmpstruct returns one of the return values above or an
  6596.         error code if an error occurred.
  6597.  
  6598. Example        #include <stdio.h>
  6599.         #include "crde.h"
  6600.  
  6601.         int tbuffers = 128;
  6602.  
  6603.         int main()
  6604.         {
  6605.           TABLE *t1, *t2;
  6606.  
  6607.           t1 = topen("sups");
  6608.           t2 = tborrow(STATIC, t1);
  6609.  
  6610.           switch (tcmpstruct(t1, t2)) {
  6611.             case 0 :
  6612.               printf("Tables are not compatible.\n");
  6613.               break;
  6614.  
  6615.  
  6616.  
  6617.  
  6618.  
  6619.                     - 107 -
  6620.  
  6621.             case 1 :
  6622.               printf("Tables are compatible but with "
  6623.             "different primary keys.\n");
  6624.               break;
  6625.             case 2 :
  6626.               printf("Tables are identical.\n");
  6627.               break;
  6628.             default :
  6629.               printf("Error %d occurred.\n", terrno);
  6630.           }
  6631.         }
  6632.  
  6633.  
  6634.  
  6635. ----------------------------------------------------------------
  6636. tcols
  6637. ----------------------------------------------------------------
  6638.  
  6639. Description      Returns the number of columns in a table.
  6640.  
  6641. Declaration      int tcols(TABLE *t);
  6642.  
  6643. Remarks          tcols returns a value from 1 to 256.
  6644.  
  6645. Return Value    tcols returns the number of columns in a table if
  6646.         successful, or an error code if an error occurred.
  6647.  
  6648. See Also         trows.
  6649.  
  6650. Example        #include <stdio.h>
  6651.         #include "crde.h"
  6652.  
  6653.         int tbuffers = 128;
  6654.  
  6655.         int main()
  6656.         {
  6657.           TABLE *t;
  6658.  
  6659.           t = topen("sups");
  6660.  
  6661.           printf("Sups table has %d columns.\n", tcols(t));
  6662.           tclose(t);
  6663.         }
  6664.  
  6665.  
  6666.  
  6667. ----------------------------------------------------------------
  6668. tcolsize
  6669. ----------------------------------------------------------------
  6670.  
  6671. Description      Returns the size of a column.
  6672.  
  6673. Declaration      int tcolsize(TABLE *t, char *c);
  6674.  
  6675. Remarks          tcolsize returns a value from 1 to 256, the size of
  6676.         the column in bytes.
  6677.  
  6678.  
  6679.                     - 108 -
  6680.  
  6681. Return Value     tcolsize returns the size of a column if successful,
  6682.         or an error code if an error occurred.
  6683.  
  6684. See Also         tcoltype, trowsize.
  6685.  
  6686. Example        #include "crde.h"
  6687.  
  6688.         int tbuffers = 128;
  6689.  
  6690.         int main()
  6691.         {
  6692.           TABLE *t;
  6693.  
  6694.           t = topen("sups");
  6695.  
  6696.           printf("Size of 'sname' column in Sups table is %d "
  6697.             "bytes.\n", tcolsize(t, "sname"));
  6698.  
  6699.           tclose(t);
  6700.         }
  6701.  
  6702.  
  6703.  
  6704. ----------------------------------------------------------------
  6705. tcoltype
  6706. ----------------------------------------------------------------
  6707.  
  6708. Description      Returns a column's type.
  6709.  
  6710. Declaration      char tcoltype(TABLE *t, char *c);
  6711.  
  6712. Remarks          tcoltype will return the character mnemonic for a
  6713.         column's datatype (either c, i, l, d, f, or $).  To
  6714.         determine the size of a column, use tcolsize.
  6715.  
  6716. Return Value     tcoltype returns the type of the column as a one
  6717.         character code if successful, or 0 if an error occured.
  6718.  
  6719. See Also         tcolsize.
  6720.  
  6721. Example        #include <stdio.h>
  6722.         #include "crde.h"
  6723.  
  6724.         int tbuffers = 128;
  6725.  
  6726.         int main()
  6727.         {
  6728.           TABLE *t;
  6729.  
  6730.           t = topen("sups");
  6731.           printf("Type of 'sname' column in Sups is ");
  6732.           switch (tcoltype(t)) {
  6733.             case 'c' : puts("char"); break;
  6734.             case 'i' : puts("int"); break;
  6735.             case 'l' : puts("long"); break;
  6736.  
  6737.  
  6738.  
  6739.                     - 109 -
  6740.  
  6741.             case 'd' : puts("date"); break;
  6742.             case 'f' : puts("double"); break;
  6743.             case '$' : puts("dollar"); break;
  6744.           }
  6745.           tclose(t);
  6746.         }
  6747.  
  6748.  
  6749.  
  6750. ----------------------------------------------------------------
  6751. tcommit
  6752. ----------------------------------------------------------------
  6753.  
  6754. Description      Makes a transaction permenent.
  6755.  
  6756. Declaration      int tcommit(TABLE *t);
  6757.  
  6758. Remarks          tcommit makes a transaction on a table permanent by
  6759.         saving all the changes made during the transaction to
  6760.         disk.
  6761.  
  6762.         tcommit will commit the transaction even if an error
  6763.         occurs.  In any event, the transaction is considered
  6764.         over.  To begin a new transaction you must call
  6765.         ttransact again.
  6766.  
  6767.         Calling tcommit outside a transaction has no effect
  6768.         on a table.
  6769.  
  6770. Return Value     tcommit returns 0 if successful, or an error code if
  6771.         an error occurred.
  6772.  
  6773. See Also         trollback, ttransact.
  6774.  
  6775. Example        #include <stdio.h>
  6776.         #include <stdlib.h>
  6777.         #include "crde.h"
  6778.  
  6779.         int tbuffers = 128;
  6780.     
  6781.         int main()
  6782.         {
  6783.           TABLE *t;
  6784.  
  6785.           t = topen("sups");
  6786.  
  6787.           /* begin transaction */
  6788.           ttransact(t);
  6789.  
  6790.           MakeChanges(t);
  6791.  
  6792.           printf("Save changes (y/n)? ");
  6793.           if (toupper(getchar()) == 'Y')
  6794.             tcommit(t);
  6795.  
  6796.  
  6797.  
  6798.  
  6799.                     - 110 -
  6800.  
  6801.           else
  6802.             trollback(t);
  6803.  
  6804.           tclose(t);
  6805.         }
  6806.  
  6807.  
  6808.  
  6809. ----------------------------------------------------------------
  6810. tcopy
  6811. ----------------------------------------------------------------
  6812.  
  6813. Description      Copies a table.
  6814.  
  6815. Declaration      TABLE *tcopy(char *name, TABLE *t);
  6816.  
  6817. Remarks          tcopy creates a copy the table and its primary key.
  6818.         Any secondary keys are not copied however.
  6819.  
  6820.         If t is a TEMP table, it will automatically be
  6821.         dropped by tcopy, even if an error occurs.  Otherwise,
  6822.         t is unaffected by tcopy.
  6823.  
  6824. Return Value     tcopy returns a pointer to an open table if successful,
  6825.         or an error code if an error occurred.
  6826.  
  6827. See Also         tborrow.
  6828.  
  6829. Example        #include <stdio.h>
  6830.         #include "crde.h"
  6831.  
  6832.         int tbuffers = 128;
  6833.  
  6834.         int main(int argc, char *argv[])
  6835.         {
  6836.           /* program to make a backup of the table specified at
  6837.           the command line; if backup name not supplied then
  6838.           "backup" is used */
  6839.  
  6840.           TABLE *t1, *t2;
  6841.           char dest[64];
  6842.  
  6843.           /* no argmuents, display help message */
  6844.           if (argc < 2) {
  6845.             printf("You must specify a table to backup.\n");
  6846.             exit(1);
  6847.           }
  6848.  
  6849.           /* get destination name */
  6850.           if (argc > 2)
  6851.             strcpy(dest, argv[2]);
  6852.           else
  6853.             strcpy(dest, "backup");
  6854.  
  6855.  
  6856.  
  6857.  
  6858.  
  6859.                     - 111 -
  6860.  
  6861.           /* open source and backup */
  6862.           if ((t1 = topen(argv[1])) != NULL) {
  6863.             if ((t2 = tcopy(dest, t1)) != NULL) {
  6864.               tclose(t2);
  6865.               printf("Backup to \"%s\" successful.\n", dest);
  6866.             }
  6867.             else
  6868.               printf("Error occurred making backup "
  6869.             "(error = %d).\n", terrno);
  6870.  
  6871.             tclose(t1);
  6872.           }
  6873.           else
  6874.             printf("Unable to open source file.\n");
  6875.         }
  6876.  
  6877.  
  6878.  
  6879. ----------------------------------------------------------------
  6880. tcount
  6881. ----------------------------------------------------------------
  6882.  
  6883. Description      Counts the number of values in a column.
  6884.  
  6885. Declaration     int tcount(TABLE *t, char *c, void *result);
  6886.  
  6887. Remarks          Because CRDE does not support NULL values, this
  6888.         function is equivelent to calling trows.  The
  6889.         result for tcount is always long, regardless of
  6890.         the column type.
  6891.  
  6892.         result is undefined is an error occurs.
  6893.  
  6894.         If t is a TEMP table, it will automatically be
  6895.         dropped by tcount, even if an error occurs.
  6896.         Otherwise, t is unaffected by tcount.
  6897.  
  6898. Return Value     tcount returns 0 if successful, or an error code
  6899.         if an error occurred.
  6900.  
  6901. See Also         taverage, tmax, tmin, tsum.
  6902.  
  6903. Example        #include "crde.h"
  6904.  
  6905.         int tbuffers = 128;
  6906.  
  6907.         int main()
  6908.         {
  6909.           TABLE *t;
  6910.           long l;
  6911.           int i;
  6912.  
  6913.           t = topen("sups");
  6914.           i = tcount(t, "s#", &l);
  6915.           if (i < 0)
  6916.             printf("Error %d occurred.\n", i);
  6917.  
  6918.  
  6919.                     - 112 -
  6920.  
  6921.           else
  6922.             printf("Table contains %ld suppliers.\n", l);
  6923.           tclose(t);
  6924.         }
  6925.  
  6926.  
  6927.  
  6928. ----------------------------------------------------------------
  6929. tcreat
  6930. ----------------------------------------------------------------
  6931.  
  6932. Description      Creates a new table.
  6933.  
  6934. Declaration      TABLE *tcreat(char *name, char *td);
  6935.  
  6936. Remarks         tcreat creates a table named name, overwriting any
  6937.         existing table by that name.  The table's structure
  6938.         is specified by the table descriptor td, an asciiz
  6939.         (null- terminated) string containing a list of column
  6940.         names and column types.  Both column names and types
  6941.         must be seperated by whitespace.  Whitespace characters
  6942.         are all those characters classified by the standard
  6943.         library function isspace() plus the comma ','.  All
  6944.         other characters (including graphical symbols) are
  6945.         significant.  Case is also significant, so Cust# and
  6946.         cust# are considered different.  This is important since
  6947.         every column name in a table must be unique.
  6948.  
  6949.         An example tcreat call might look like
  6950.  
  6951.         t = tcreat("sups", "*s# i, sname c26, city c26, status i");
  6952.  
  6953.         Every column name must be followed by a datatype.
  6954.         CRDE provides six basic datatypes common to virtually
  6955.         all database managers. In addition, all datatypes are
  6956.         analogous to native C datatypes which makes for easy
  6957.         transition from CRDE tables to C variables.
  6958.  
  6959.         Here is a list of the datatypes supported by CRDE each
  6960.         followed by a description of the datatype, and its C
  6961.         equivelent:
  6962.  
  6963.               Type    Description        C equivelent
  6964.           
  6965.               cXXX      array of up to 256    char[ XXX]
  6966.                 characters. XXX is a 
  6967.             positive number 
  6968.             between 1 and 256.
  6969.  
  6970.               i         integer (whole         int
  6971.             numbers,  + and -)
  6972.  
  6973.         l         long integer               long int
  6974.  
  6975.         d        date                       1date_ t
  6976.  
  6977.  
  6978.  
  6979.                     - 113 -
  6980.  
  6981.         f         floating-point             double
  6982.  
  6983.         $         dollar value               1dollar_t
  6984.  
  6985.  
  6986.         1Date and dollar types have no direct C equivelents
  6987.         but are represented as C types by the typedefs date_t
  6988.         and dollar_t defined in CRDE.  Typical representations
  6989.         are long int for date_t and double for dollar_t
  6990.         although you should always use the date_t and
  6991.         dollar_t symbols for portability reasons.
  6992.  
  6993.  
  6994.         Defining a primary key.
  6995.  
  6996.         Any column name directly preceded with the '*'
  6997.         character is considered to be part of the primary
  6998.         key. There must be no whitespace between the '*' and
  6999.         the column name.  Up to 8 columns may be part of the
  7000.         primary key.  Although CRDE tables are not required
  7001.         to have a primary key, it is generally good relational
  7002.         practice to do so.  Defining a primary key on a table
  7003.         prevents two rows in a table which have identical values
  7004.         in every column that is marked with an asterisk '*'.  In
  7005.         addition, defining a primary key may have performance
  7006.         benefits as well (see Chapter 3, Creating Tables).
  7007.         However there is a catch:  a primary key makes a table
  7008.         larger, using up more disk space.
  7009.  
  7010. Return Value     tcreat returns a pointer to an open table if successful,
  7011.         or NULL if an error occurs.
  7012.  
  7013. See Also        topen.
  7014.  
  7015. Example:          #include <stdio.h>
  7016.               #include "crde.h"
  7017.  
  7018.               typedef struct {
  7019.           int no;
  7020.           char name[26];
  7021.           int age;
  7022.           dollar_t salary;
  7023.         } emp;
  7024.  
  7025.         int tbuffers = 128;
  7026.  
  7027.               int main()
  7028.               {
  7029.           TABLE *t;
  7030.           emp e;
  7031.  
  7032.           /* create a simple employee table */
  7033.           t = tcreat("emps", "*e# i, name c26, age i, salary $");
  7034.  
  7035.  
  7036.  
  7037.  
  7038.  
  7039.                     - 114 -
  7040.  
  7041.           /* add a record to the table */
  7042.           e.no = 1;
  7043.           strcpy(e.name, "John Smith");
  7044.           e.age = 35;
  7045.           e.salary = 33000.0;
  7046.  
  7047.           tinsert(t, &e, 1);
  7048.  
  7049.           /* close the table */
  7050.           tclose(t);
  7051.         }
  7052.  
  7053.  
  7054.  
  7055. ----------------------------------------------------------------
  7056. tcrstruct
  7057. ----------------------------------------------------------------
  7058.  
  7059. Description      Creates a table from struct table.
  7060.  
  7061. Declaration      TABLE *tcrstruct(char *name, TABLE *t);
  7062.  
  7063. Remarks          Instead of using a table descriptor to create a new
  7064.         table, tcrstruct uses a table containing column name
  7065.         and type information similar to the one generated by
  7066.         tstruct.
  7067.  
  7068.         The only requirements which tcrstruct makes of the
  7069.         struct table t is that its first two columns be of
  7070.         type c (char).  The first column should hold the
  7071.         column names while the second should hold the column
  7072.         type.  Other than that, there are no other restrictions
  7073.         on the structure of t.
  7074.  
  7075.         The columns in the new table will appear in the same
  7076.         order that they were physically inserted in the struct
  7077.         table.
  7078.  
  7079.         tcrstruct if useful in that it can be used by programs
  7080.         to create table structures dynamically or even
  7081.         interactively.
  7082.  
  7083.         If t is a TEMP table, it will be automatically dropped
  7084.         by tcrstruct, even if an error occurs.  Otherwise, t is
  7085.         unaffected by tcrstruct.
  7086.  
  7087. Return Value     tcrstruct returns a pointer to an open table if
  7088.         successful, or NULL if an error occurred.
  7089.  
  7090. Example          #include <stdio.h>
  7091.               #include "crde.h"
  7092.  
  7093.         int tbuffers = 128;
  7094.  
  7095.  
  7096.  
  7097.  
  7098.  
  7099.                     - 115 -
  7100.  
  7101.               int main()
  7102.               {
  7103.           TABLE *t, *struc;
  7104.           char name[64], cname[32], ctype[5];
  7105.  
  7106.           struc = tcreat(STATIC, "*colname c18, coltype c5");
  7107.  
  7108.           if (struc == NULL) {
  7109.             printf("Unable to create struct table.\n");
  7110.             exit(1);
  7111.           }
  7112.  
  7113.           printf("Table name: ");
  7114.           gets(name);
  7115.           while (1) {
  7116.             printf("\nColumn name: ");
  7117.             gets(cname);
  7118.             if (strcmp(cname, "") == 0)
  7119.               break;
  7120.             printf("Column type: ");
  7121.             gets(ctype);
  7122.             tload(struc, cname, ctype);
  7123.           }
  7124.  
  7125.           t = tcrstruct(name, struc);
  7126.           if (t == NULL) {
  7127.             printf("Error: unable to create new table.\n");
  7128.             exit(1);
  7129.           }
  7130.  
  7131.           printf("\nTable \"%s\" created.\n", name);
  7132.           printf("Table has %d columns totalling %d bytes in "
  7133.             "size.\n", tcols(t), trowsize(t));
  7134.  
  7135.           if (tkeyed(t))
  7136.             printf("Table is keyed.\n");
  7137.           else
  7138.             printf("Table is not keyed.\n");
  7139.  
  7140.           tclose(t);
  7141.           tdrop(struc);
  7142.         }
  7143.  
  7144. Output:           Table name: dummy
  7145.  
  7146.               Column name: *cust#
  7147.               Column type: i
  7148.  
  7149.               Column name: name
  7150.               Column type: c26
  7151.  
  7152.               Column name: phone
  7153.               Column type: c13
  7154.  
  7155.               Column name:
  7156.  
  7157.  
  7158.  
  7159.                     - 116 -
  7160.  
  7161.               Table "dummy" created.
  7162.         Table has 3 columns totalling 41 bytes in size.
  7163.         Table is keyed.
  7164.  
  7165.  
  7166.  
  7167. ----------------------------------------------------------------
  7168. tdelete
  7169. ----------------------------------------------------------------
  7170.  
  7171. Description      Deletes records from a table.
  7172.  
  7173. Declaration      long tdelete(TABLE *t, ...);
  7174.  
  7175. Remarks          tdelete expects a series of arguments which form a
  7176.         search expression like the one described in tselect.
  7177.         Any records matching the search expression are deleted
  7178.         from the table.
  7179.  
  7180.         Note:  tdelete does not physically delete rows from
  7181.         a table.  Rather it marks the space as unused, and is
  7182.         reclaimed automatically when you re-insert rows into
  7183.         the table.
  7184.  
  7185. Return Value     tdelete returns the number of rows deleted if successful,
  7186.         or an error code if an error occurred.
  7187.  
  7188. See Also         tdeleteif, tinsert, tselect.
  7189.  
  7190. Example        int DeleteCust(int supno) 
  7191.         {
  7192.           /* delete a supplier from the sups table; deletes any
  7193.           orders made by that supplier to maintain referential
  7194.           integrity */
  7195.  
  7196.           int i;
  7197.  
  7198.           i = tdelete(sups, "s#", EQ, supno, NULL);
  7199.           if  (i > 0)
  7200.             i = tdelete(orders, "s#", EQ, supno, NULL);
  7201.           return i;
  7202.         }
  7203.  
  7204.  
  7205.  
  7206. ----------------------------------------------------------------
  7207. tdeleteif
  7208. ----------------------------------------------------------------
  7209.  
  7210. Description      Deletes records from a table.
  7211.  
  7212. Declaration      long tdeleteif(TABLE *t, int (*action)(void *), ...);
  7213.  
  7214.  
  7215.  
  7216.  
  7217.  
  7218.  
  7219.                     - 117 -
  7220.  
  7221. Remarks          tdeleteif is exactly like tdelete except it accepts an
  7222.         additional parameter action.  Every row which matches
  7223.         the search expression is passed to action for further
  7224.         evaluation.  action should behave exactly as the action
  7225.         described in tselectif:
  7226.  
  7227.         Result of action    What happens
  7228.  
  7229.             > 0              Row is deleted.
  7230.  
  7231.                  = 0              Row is not deleted.
  7232.  
  7233.             < 0              An error occurred.
  7234.                     tdeleteif is aborted and
  7235.                     returns the result of action.
  7236.  
  7237.               See tselectif for more details on action.
  7238.  
  7239. Return Value     tdeleteif returns the number of rows deleted if
  7240.         successful, or an error code if and error occurred.
  7241.  
  7242. See Also         tdelete, tselect, tselectif.
  7243.  
  7244. Example        /* routine to delete sups who live in either Atlanta
  7245.         or Boston */
  7246.  
  7247.         int DeleteSups(void *rec)
  7248.         {
  7249.           return (strcmp(((sup *)rec)->city, "Boston) == 0 ||
  7250.             strcmp(((sup *)rec)->city, "Atlanta") == 0;
  7251.         }
  7252.  
  7253.         tdeleteif(sups, DeleteSups, NULL);
  7254.  
  7255.  
  7256.  
  7257. ----------------------------------------------------------------
  7258. tdiff
  7259. ----------------------------------------------------------------
  7260.  
  7261. Description      Returns the relational difference of two tables.
  7262.  
  7263. Declaration      TABLE *tdiff(char *name, TABLE *t1, TABLE *t2);
  7264.  
  7265. Remarks          tdiff returns the relational difference of tables
  7266.         t1 and t2.  The result table will have a structure
  7267.         identical to t1 and contain all the records found
  7268.         in t1 not found in t2.
  7269.  
  7270.         tdiff requires that t1 and t2 be compatible, i.e.
  7271.         that they be equivelent on a column by column basis
  7272.         in both type and size.  However they may have
  7273.         different primary or secondary keys.  tdiff works
  7274.         differently depending upon whether t1 is keyed or
  7275.         not.  If t1 is keyed then tdiff will return every
  7276.  
  7277.  
  7278.  
  7279.                     - 118 -
  7280.  
  7281.         row in t1 whose key values does not match the key
  7282.         values of any row in t2.  If t1 is not keyed, then
  7283.         only those rows which do not entirely match any row
  7284.         in t2 will be returned.
  7285.  
  7286.         If t1 or t2 is a TEMP table, then tdiff will drop
  7287.         either or both of them, respectively.  Otherwise, t1
  7288.         and t2 are unaffected by tdiff.
  7289.  
  7290. Return Value     tdiff returns a pointer to an open table if successful,
  7291.         or NULL if an error occurred.
  7292.  
  7293. See Also         tintersect.
  7294.  
  7295. Example        /* return a list of all suppliers who have not made
  7296.         any orders */
  7297.  
  7298.         answer = tdiff(STATIC,
  7299.           tproject(TEMP, sups, "s#", NULL),
  7300.           tproject(TEMP, orders, "*s#", NULL)
  7301.         );
  7302.  
  7303.  
  7304.  
  7305. ----------------------------------------------------------------
  7306. tdrop
  7307. ----------------------------------------------------------------
  7308.  
  7309. Description     Closes and deletes a table.
  7310.  
  7311. Declaration      int tdrop(TABLE *t);
  7312.  
  7313. Remarks         tdrop closes and subsequently deletes a table and all
  7314.         its indexes on disk.  All memory held by the table is
  7315.         released.  Because the table is being deleted, any
  7316.         unwritten buffers are simply released. If the table
  7317.         was in a transaction, it is rolled back, before the
  7318.         table is dropped.
  7319.  
  7320.         You can also drop temporary tables (TEMP and STATIC.)
  7321.         Dropping and closing a temporary table are functionally
  7322.         equivelent.
  7323.  
  7324.         tdrop drops the table, even if an error occurs.
  7325.  
  7326. Return Value     tdrop returns 0 if successful, or an error code if an
  7327.         error occurred.
  7328.  
  7329. See Also        tclose.
  7330.  
  7331. Example        #include <stdio.h>
  7332.         #include "crde.h"
  7333.  
  7334.         int tbuffers = 128;
  7335.  
  7336.  
  7337.  
  7338.  
  7339.                     - 119 -
  7340.  
  7341.         int main()
  7342.         {
  7343.           TABLE *t, *t1;
  7344.  
  7345.           /* "Packs" the sups table */
  7346.  
  7347.           t = topen("sups");
  7348.           if ((t1 = tcopy("dummy")) != NULL) {
  7349.             tdrop(t);
  7350.             tclose(t1);
  7351.             trename("dummy", "sups");
  7352.           }
  7353.           else
  7354.             tclose(t);
  7355.         }
  7356.  
  7357.  
  7358.  
  7359. ----------------------------------------------------------------
  7360. tdropindex
  7361. ----------------------------------------------------------------
  7362.  
  7363. Description      Drops a secondary index from a table.
  7364.  
  7365. Declaration      int tdropindex(TABLE *t, char *id);
  7366.  
  7367. Remarks          tdropindex removes a secondary index from a table.
  7368.         td is an index descriptor like the one described in
  7369.         tindex.  The index described must exist to be dropped.
  7370.  
  7371.         tdropindex works only with secondary indexes--you
  7372.         cannot drop a primary index.
  7373.  
  7374. Return Value     tdropindex return 0 if successful, or an error code
  7375.         if an error occured.
  7376.  
  7377. See Also        tindex.
  7378.     
  7379. Example        #include "crde.h"
  7380.  
  7381.         int main()
  7382.         {
  7383.           TABLE *t;
  7384.  
  7385.           t = topen("sups");
  7386.           tdropindex(t, "sname");
  7387.           tclose(t);
  7388.         }
  7389.  
  7390.  
  7391.  
  7392.  
  7393.  
  7394.  
  7395.  
  7396.  
  7397.  
  7398.  
  7399.                     - 120 -
  7400.  
  7401. ----------------------------------------------------------------
  7402. tempty
  7403. ----------------------------------------------------------------
  7404.  
  7405. Description      Removes all rows from a table.
  7406.  
  7407. Declaration      int tempty(TABLE *t);
  7408.  
  7409. Remarks          tempty removes all rows from the table.
  7410.  
  7411.         tempty is superior to tdelete for deleting all rows
  7412.         from a table for a couple of reasons.  tempty
  7413.         physically reclaims all deleted space from the table.
  7414.         In addition, tempty is much faster than tdelete.
  7415.  
  7416. Return Value     tempty returns 0 if successful, or an error code if
  7417.         an error occured.
  7418.  
  7419. See Also         tdelete.
  7420.  
  7421.  
  7422.  
  7423. ----------------------------------------------------------------
  7424. terase
  7425. ----------------------------------------------------------------
  7426.  
  7427. Description      Erases a table.
  7428.  
  7429. Declaration      int terase(char *name);
  7430.  
  7431. Remarks          terase erases the named table and all associated
  7432.         indexes.
  7433.  
  7434.         Never erase an open table, use tdrop instead.
  7435.  
  7436. Return Value     terase returns 0 if successful or -1 if an error
  7437.         occured.
  7438.  
  7439. See Also         tdrop.
  7440.  
  7441.  
  7442.  
  7443. ----------------------------------------------------------------
  7444. texportascii
  7445. ----------------------------------------------------------------
  7446.  
  7447. Description      Exports a table to an ascii file.
  7448.  
  7449. Declaration      long texportascii(TABLE *t, char *dest);
  7450.  
  7451. Remarks         texportascii exports a CRDE table to an ascii file.
  7452.         t is the table to be exported.  dest is the filename
  7453.         of the ascii file to export to.  dest is automatically
  7454.         overwritten by texportascii.
  7455.  
  7456.  
  7457.  
  7458.  
  7459.                     - 121 -
  7460.  
  7461.         texportascii writes columns to the ascii file in
  7462.         the order in which they appear in t.   A comma
  7463.         seperates each column in the record, and each record
  7464.         appears on an individual line of the ascii file.
  7465.         Each column is output in a format according to its
  7466.         datatype:
  7467.  
  7468.               CRDE
  7469.         type    Format of column in ascii file
  7470.  
  7471.         c          CRDE will output a character string enclosed
  7472.             in quotes "".
  7473.  
  7474.         i          CRDE will output a base 10 integer.  Negative
  7475.             numbers will be preceded with a negative sign -.
  7476.  
  7477.         l          CRDE will output a base 10 integer. Negative
  7478.             numbers will be preceded with a negative sign -.
  7479.  
  7480.         d          Dates are output in the format mm/dd/yyyy and
  7481.             enclosed in quotes.
  7482.  
  7483.         f    Floating point numbers are output with 4 decimal
  7484.             places.  Negative numbers are preceded with a
  7485.             negative sign.
  7486.  
  7487.         $          Dollar values are output with 2 decimal places.
  7488.             Negative numbers are preceded with a negative
  7489.             sign.
  7490.  
  7491. Return Value     texportascii returs the number of rows exported if
  7492.         successful, or an error code if an error occurred.
  7493.  
  7494. See Also        timportascii.
  7495.  
  7496. Example:          #include <stdio.h>
  7497.         #include "crde.h"
  7498.  
  7499.         int tbuffers = 128;
  7500.  
  7501.         int main()
  7502.         {
  7503.           TABLE *t;
  7504.           long result;
  7505.  
  7506.           t = topen("sups");
  7507.  
  7508.           result = texportascii(t,  "sups.txt");
  7509.           if (result < 0)
  7510.             printf("Export failed (error code %d "
  7511.               "occurred.\n", terrno);
  7512.           else
  7513.             printf("Export successful.  %ld rows "
  7514.               "exported.\n", result);
  7515.  
  7516.           tclose(t);
  7517.         }
  7518.  
  7519.                     - 122 -
  7520.  
  7521. ----------------------------------------------------------------
  7522. texportdBASE
  7523. ----------------------------------------------------------------
  7524.  
  7525. Description     Exports a table to dBASE III Plus format.
  7526.  
  7527. Desclaration    long texportdBASE(TABLE *t, char *dest);
  7528.  
  7529. Remarks        texportdBASE exports a CRDE table to a dBASE III
  7530.         Plus file named dest.  texportdBASE does not assume
  7531.         the ".DBF" extension so you must add this on your
  7532.         own.
  7533.  
  7534.         texportdBASE only exports the actual table.  No
  7535.         indexes, primary or secondary, are exported.
  7536.  
  7537.         texportdBASE translates CRDE columns into equivelent
  7538.         dBASE fields.  Column names are converted to
  7539.         uppercase and placed in the dBASE header in the same
  7540.         order they appear in the CRDE table.  Columns are
  7541.         converted into the smallest dBASE type which can
  7542.         accurately hold the data.
  7543.  
  7544.         CRDE
  7545.         type    How CRDE column is converted to dBASE.
  7546.  
  7547.         c     Character field with a length equaling the
  7548.             length of the c column - 1.
  7549.  
  7550.         i     Numeric field with a length of 6 and 0 decimal
  7551.             places.
  7552.  
  7553.         
  7554.         l    Numeric field with a length of 11 and 0 decimal
  7555.             places.
  7556.  
  7557.         d      CRDE dates are converted to dBASE format.
  7558.  
  7559.         f      Numeric field with a length of 19 and 4 decimal
  7560.             places.
  7561.  
  7562.         $      Numeric field with a length of 19 and 2 decimal
  7563.             places.
  7564.  
  7565. Return Value    texportdBASE returns the number of rows exported or an
  7566.         error code if an error occurred.
  7567.  
  7568. See Also    timportdBASE.
  7569.  
  7570.  
  7571.  
  7572.  
  7573.  
  7574.  
  7575.  
  7576.  
  7577.  
  7578.  
  7579.                     - 123 -
  7580.  
  7581. ----------------------------------------------------------------
  7582. tflush
  7583. ----------------------------------------------------------------
  7584.  
  7585. Description      Flushes a table's buffers.
  7586.  
  7587. Declaration      int tflush(TABLE *t);
  7588.  
  7589. Remarks          tflush flushes any unwritten buffers to disk.
  7590.  
  7591.         How tflush responds depends on the particular mode a
  7592.         table is in:
  7593.  
  7594.         T_NORMAL    tflush will have no effect since CRDE
  7595.                 automatically flushes all changes after
  7596.                 every function call in normal mode.
  7597.  
  7598.         T_WRITEC    All unwritten buffers are flushed to
  7599.                 disk.
  7600.  
  7601.         T_STATIC         All unwritten buffers are flushed to
  7602.                 disk.  If the temporary table does not
  7603.                 yet have an image on disk, tflush will
  7604.                 force the creation of one automatically.
  7605.  
  7606.         T_TEMP        Same as T_STATIC.
  7607.  
  7608. Return Value     tflush returns 0 if succesful, of an error code if an
  7609.         error occurred.
  7610.  
  7611. See Also         twritec, tnormal, trelease.
  7612.  
  7613.  
  7614.  
  7615. ----------------------------------------------------------------
  7616. tget
  7617. ----------------------------------------------------------------
  7618.  
  7619. Description      Reads rows from a table into an array.
  7620.  
  7621. Declaration      int tget(TABLE *t, void *array, int n, ...);
  7622.  
  7623. Remarks        tget reads up to n rows from t and copies them into
  7624.         the buffer pointed to by array.  The buffer should
  7625.         be an array of structures similar to the structure
  7626.         of t and should be large enough to hold at least n
  7627.         rows.
  7628.  
  7629.         tget expects a series of arguments which form a search
  7630.         expression like the one described in tselect.  Only
  7631.         those records which match the search criteria are
  7632.         placed in the buffer.  tget stops after the entire
  7633.         table has been scanned or n records are retreived,
  7634.         whichever occurs first.  The order in which the
  7635.         records are retreived is undefined unless there no
  7636.  
  7637.  
  7638.  
  7639.                     - 124 -
  7640.  
  7641.         search expression and the physical order of the records
  7642.         in the table is known (for example, after a tsort
  7643.         function call.)
  7644.  
  7645.         Note that the rows retreived are only copies of the
  7646.         records in the table.  Rows are not removed from the
  7647.         table by tget, nor will changing the rows retreived
  7648.         in the buffer have any effect on the rows actually in
  7649.         t.
  7650.  
  7651. Return Value     tget returns the number of rows retreived if successful,
  7652.         or an error code if an error occurred.
  7653.  
  7654. See Also         tgetif, tselect.
  7655.  
  7656. Example:          /* load some suppliers into a buffer */
  7657.  
  7658.         sup buf[50];
  7659.  
  7660.         sups = topen("sups");
  7661.         tget(sups, buf, 50, "city", EQ, "Atlanta", NULL);
  7662.  
  7663.  
  7664.  
  7665. ----------------------------------------------------------------
  7666. tgetif
  7667. ----------------------------------------------------------------
  7668.  
  7669. Description      Reads rows from a table into an array.
  7670.  
  7671. Declaration      int tget(TABLE *t, void *array, int n,
  7672.           int (*action)(void *), ...);
  7673.  
  7674. Remarks          tgetif is exactly like tget except it accepts an
  7675.         additional parameter action.  Every row which matches
  7676.         the search expression is passed to action for further
  7677.         evaluation.  action should behave exactly as the
  7678.         action described in tselectif:
  7679.  
  7680.               Result of action    What happens
  7681.  
  7682.             > 0              Row is retrieved.
  7683.  
  7684.                  = 0             Row is not retrieved.
  7685.  
  7686.             < 0              An error occurred.  tgetif is
  7687.                     aborted and returns the result
  7688.                     of action.
  7689.  
  7690.               See tselectif for more details on action.
  7691.  
  7692. Return Value     tget returns the number of rows retreived if
  7693.         successful, or an error code if an error occurred.
  7694.  
  7695. See Also         tget, tselect, tselectif.
  7696.  
  7697.  
  7698.  
  7699.                     - 125 -
  7700.  
  7701. ----------------------------------------------------------------
  7702. tgroup
  7703. ----------------------------------------------------------------
  7704.  
  7705. Description      Groups a table on one or more columns.
  7706.  
  7707. Declaration      TABLE *tgroup(char *name, TABLE *t, char *groupby, char
  7708.           *td, int (*summary) (void *in, void *out, int first);
  7709.  
  7710. Remarks        tgroup is an extremely powerful function which organzies
  7711.         a table into groups and allows you to perform
  7712.         calculations upon those groups by writing a special
  7713.         grouping function.
  7714.  
  7715.         tgroup creates a table name from the source table t.
  7716.         groupby is a column list containing the columns to
  7717.         group on.  For example, "city" would group on the city
  7718.         column, while "city,status" would group on both the
  7719.         city and status columns.  Up to 8 columns may be
  7720.         specified in groupby.  td is a table descriptor which
  7721.         describes the output table.  It is up to the group
  7722.         function summary  to generate a table which corresponds
  7723.         to td.
  7724.  
  7725.         summary is a user-defined function which creates the
  7726.         result table.  It accepts 3 parameters in (void *),
  7727.         out (void *), and first (int), and must return an int.
  7728.  
  7729.         in points to the next record in t being passed to summary.
  7730.  
  7731.         out points to the record currently being generated by
  7732.         summary.  It has the same structure as described in td.
  7733.  
  7734.         first determines the position of the row (pointed to by
  7735.         in) within that particular group.  If first is 1 then
  7736.         the row is the first in that group, or 0 if it is not.
  7737.         After every row in the group has been processed, tgroup
  7738.         calls summary once more with first set to -1.  This
  7739.         allows summary to do any clean up or overall calculations
  7740.         on the group.
  7741.  
  7742.  
  7743.         Writing grouping functions.
  7744.  
  7745.         A group function should have a declaration similar to
  7746.  
  7747.         int groupfunc(void *in, void *out, int first);
  7748.  
  7749.         Every row in each group is passed to groupfunc and is
  7750.         referenced  by in.  The information in used to calculate
  7751.         theoutput row for the group.  out is a pointer to the
  7752.         output row for a particular group.  groupfunc is
  7753.         responsible for building out.  first is a parameter
  7754.         used to determine whether a row is the first or last
  7755.         element in the group.  The information is used to
  7756.         determine when to initialize the group and whether to
  7757.  
  7758.  
  7759.                     - 126 -
  7760.  
  7761.         include the group in the answer table.  The following
  7762.         describes how tgroup uses groupfunc to generate the
  7763.         answer table.
  7764.  
  7765.         1. tgroup calls the groupfunc with first == 1.  in
  7766.         points to the first row in the group.  The groupfunc
  7767.         is expected to initialize out.
  7768.  
  7769.         2. tgroup calls groupfunc with first == 0 for every
  7770.         other row in the group.  Each row is accessed through
  7771.         the pointer in.  groupfunc should update any
  7772.         calculated columns in out based   on each row in the
  7773.         group.
  7774.  
  7775.         3.  After every row in the group has been passed to
  7776.         groupfunc, tgroup calls groupfunc once more time
  7777.         with first == -1.  groupfunc should perform any final
  7778.         calculations (now that all rows in the group have
  7779.         been processed), and do any cleanup necessary.  out
  7780.         should now contain the values which will appear in
  7781.         the answer table.  However, groupfunc can also decide
  7782.         whether or not it wants to include out in the answer
  7783.         table.  If groupfunc returns > 0 then tgroup will
  7784.         include out.  If groupfunc returns 0 then tgroup will
  7785.         not include the row.
  7786.  
  7787.         tgroup repeats step 1-3 for every group in the table.
  7788.         In steps 1. and 2., groupfunc should return a value
  7789.         >= 0 if no errors occurred.  Regardless of the value
  7790.         of first, if the groupfunc returns a value < 0, tgroup
  7791.         aborts and returns NULL.  terrno will be set to the
  7792.         value returned by groupfunc.  A well designed groupfunc
  7793.         should include error-handling and perform any necessary
  7794.         cleanup if an error occurs.
  7795.  
  7796.         Complete details on writing group functions and full
  7797.         examples are provided in Chapter 12, Performing Queries.
  7798.  
  7799. Return Value     tgroup returns a pointer to an open table if successful,
  7800.         or NULL if an error occurred.
  7801.  
  7802. Example          /* Simply group function to count suppliers in each
  7803.         city in the sups table */
  7804.  
  7805.         int CountSups(void *in, void *out, int first)
  7806.         {
  7807.           typedef struct {
  7808.             char city[26];
  7809.             int count;
  7810.           } gs;
  7811.  
  7812.           switch (first) {
  7813.             case 1:
  7814.               strcpy(((gs *)out)->city, ((sup *)in)->city);
  7815.               ((gs *)out)->count = 0;
  7816.  
  7817.  
  7818.  
  7819.                     - 127 -
  7820.  
  7821.             case 0:
  7822.               ((gs *)out)->count++;
  7823.               break;
  7824.           }
  7825.  
  7826.           return 1;
  7827.         }
  7828.  
  7829.         answer = tgroup(STATIC, sups, "city", "city c26, count i",
  7830.           CountSups);
  7831.  
  7832.  
  7833.  
  7834. ----------------------------------------------------------------
  7835. timportascii
  7836. ----------------------------------------------------------------
  7837.  
  7838. Description      Imports an ascii file.
  7839.  
  7840. Declaration      TABLE *timportascii(char *name, char *td, char *source);
  7841.  
  7842. Remarks:        timportascii imports an ascii file into a CRDE table.
  7843.         td is a table descriptor which describes the table.
  7844.         source if the file name of the ascii file to import.
  7845.  
  7846.         timportascii reads columns from the ascii file in the
  7847.         order in which they appear in td.  A comma should
  7848.         seperate each column in the record, and each record
  7849.         should appear on an individual row of the ascii file.
  7850.         In addition, each column must conform to the following
  7851.         format, dependent upon the datatype of the column
  7852.         being imported:
  7853.  
  7854.         CRDE
  7855.         type    Expected format of column in ascii file
  7856.  
  7857.         c     A character string contained in quotes "".
  7858.             If the string is too large for the column, it
  7859.             will be truncated to fit.
  7860.  
  7861.  
  7862.         i          A base 10 integer is expected. Negative values
  7863.             should be preceded with a negative sign -.  If
  7864.             the integer is out of range for the column,
  7865.             the value for that column is undefined.
  7866.  
  7867.         l          A base 10 integer is expected.  Negative values
  7868.             should be preceded with a negative sign -.  If
  7869.             the integer is out of range for the column, the
  7870.             value for that column is undefined.
  7871.  
  7872.         d          A date in the format of mm/dd/yyyy is expected.
  7873.             The date must be enclosed in quotes.  Whitespace
  7874.             is ignored.
  7875.  
  7876.  
  7877.  
  7878.  
  7879.                     - 128 -
  7880.  
  7881.         f          A floating point value with any number of decimal
  7882.             places.  Exponential notation may be used, +/-eNN.
  7883.             Negative numbers must be preceded with a negative
  7884.             sign -.  If the number is out of range, the
  7885.             resulting value imported is undefined.
  7886.  
  7887.         $          Same as f.
  7888.  
  7889. Return Value     timportascii returns a pointer to an open table if
  7890.         successful, or NULL if an error occurred.
  7891.  
  7892. See Also        texportascii.
  7893.  
  7894. Example        #include "crde.h"
  7895.  
  7896.         int tbuffers = 128;
  7897.  
  7898.         int main()
  7899.         {
  7900.           TABLE *t;
  7901.  
  7902.           /* Program to import the sups table from a pre-
  7903.           existing ascii file. */
  7904.  
  7905.           t = timportascii("sups", "*s# i, sname c26, city c26, "
  7906.             "status i", "sups.dat");
  7907.  
  7908.           tclose(t);
  7909.         }
  7910.  
  7911.  
  7912.  
  7913. ----------------------------------------------------------------
  7914. timportdBASE
  7915. ----------------------------------------------------------------
  7916.  
  7917. Description    Imports a dBASE III or dBASE III Plus data file.
  7918.  
  7919. Declaration    TABLE *timportdBASE(char *name, char *source);
  7920.  
  7921. Remarks        timportdBASE imports a dBASE III or dBASE III Plus
  7922.         compatible data file.  source  is the name the the
  7923.         dBASE file to import.  timportdBASE does not assume
  7924.         the ".DBF" extension so you must remember to add it
  7925.         yourself.
  7926.  
  7927.         timportdBASE only inports dBASE data files.  It cannot
  7928.         import dBASE indexes or memo files.  timportdBASE
  7929.         determines the CRDE table structure by reading the
  7930.         dBASE file header and converting the fields to
  7931.         CRDE equivelents.
  7932.  
  7933.  
  7934.  
  7935.  
  7936.  
  7937.  
  7938.  
  7939.                     - 129 -
  7940.  
  7941.         dBASE
  7942.         type    How dBASE field is converted to CRDE
  7943.  
  7944.         C          c column equaling length of dBASE field + 1.
  7945.  
  7946.         D          dBASE's dates are converted to CRDE's date
  7947.             format.
  7948.  
  7949.         N         If the number of decimal places equals 2 then
  7950.             the colum type is $.  If decimal places is > 0
  7951.             then column is type f.  If the length of the
  7952.             number field is > 11 then the column is type f.
  7953.             If the length of the number field is < 7 then
  7954.             the column is type i.  Otherwise the column is
  7955.             type l.
  7956.  
  7957.         L          Logical fields are converted to type i. If the
  7958.             field is 'Y' or 'T' the column will have a value
  7959.             of 1, otherwise 0.
  7960.  
  7961.  
  7962.         M          CRDE does import memo fields.  Any memo fields
  7963.             in the dBASE file are ignored.
  7964.  
  7965. Return Value    timportdBASE returns a pointer to an open table if
  7966.         successful, or NULL if an error occurs.
  7967.  
  7968. See Also    texportdBASE.
  7969.  
  7970. Example        #include <stdio.h>
  7971.         #include "crde.h"
  7972.  
  7973.         int tbuffers = 128;
  7974.  
  7975.         int main()
  7976.         {
  7977.           TABLE *t;
  7978.  
  7979.           t = timportdBASE("Cust", "Cust.dbf");
  7980.           if (t) {
  7981.             printf("dBASE import successful.  %ld rows"
  7982.               " imported.\n");
  7983.             tclose(t);
  7984.           }
  7985.           else
  7986.             printf("Import failed (error code = %d).\n", terrno);
  7987.         }
  7988.  
  7989.  
  7990.  
  7991.  
  7992.  
  7993.  
  7994.  
  7995.  
  7996.  
  7997.  
  7998.  
  7999.                     - 130 -
  8000.  
  8001. ----------------------------------------------------------------
  8002. tindex
  8003. ----------------------------------------------------------------
  8004.  
  8005. Description:       Creates a secondary index.
  8006.  
  8007. Declaration:      int tindex(TABLE *t, char *id);
  8008.  
  8009. Remarks:         tindex creates a secondary index on a table specified
  8010.         by the index descriptor id.  id is an asciiz (null-
  8011.         terminated) string of column names seperated by
  8012.         whitespace.  Whitespace consists of all characters
  8013.         classified by the isspace() function plus the comma
  8014.         ','.  id cannot contain more than 8 columns, nor can
  8015.         a column occur more than once in the descriptor.
  8016.  
  8017.  
  8018.         Descending keys.
  8019.  
  8020.         You can mark any of the columns to be descending keys
  8021.         by prepending the column name with an exclaimation
  8022.         point '!'.  Normally, the only time you would use
  8023.         descending keys is in describing the sort order in a
  8024.         tsort function.  Although it is completely legal to
  8025.         specify descending keys in normal indexes, it is
  8026.         rarely useful to do so.  Using either ascending or
  8027.         descending keys in an index has no effect on
  8028.         performance. CRDE handles both kinds of keys
  8029.         transparently.
  8030.  
  8031.         Once the secondary index is created, it is
  8032.         automatically maintained by CRDE. It is opened and
  8033.         closed whenever along with the table and updated
  8034.         whenever records are inserted,changed, or deleted.
  8035.         Unlike the primary index, a secondary index may contain
  8036.         duplicates, so it cannot be used to prevent duplicate
  8037.         keys in the table (only the primary index can do that.)
  8038.         Secondary indexes are mostly used for speeding up
  8039.         various CRDE operations.  Since CRDE knows all about
  8040.         your indexes, it can use them to help in searches,
  8041.         sorting, joining, etc. to significantly improve
  8042.         performance.
  8043.  
  8044.         Calling tindex on an index which already exist rebuilds
  8045.         that particular index.  One drawback is that tindex
  8046.         only works with secondary indexes, you can't rebuild
  8047.         the primary index with tindex.
  8048.  
  8049. Return Value    tindex returns 0 if successful, or an error code if an
  8050.         error occurred.
  8051.  
  8052. See Also       tdropindex.
  8053.  
  8054.  
  8055.  
  8056.  
  8057.  
  8058.  
  8059.                     - 131 -
  8060.  
  8061. Example        #include "crde.h"
  8062.  
  8063.         int tbuffers = 128;
  8064.  
  8065.         int main()
  8066.         {
  8067.           TABLE *t;
  8068.  
  8069.           /* create index on "sname" on Sups table. */
  8070.  
  8071.           t = topen("sups");
  8072.           tindex(t, "sname");
  8073.           tclose(t);
  8074.         }
  8075.  
  8076.  
  8077.  
  8078. ----------------------------------------------------------------
  8079. tindexes
  8080. ----------------------------------------------------------------
  8081.  
  8082. Description      Calculates the number of secondary indexes.
  8083.  
  8084. Declaration      int tindexes(TABLE *t);
  8085.  
  8086. Remarks        tindexes returns the number of secondary indexes on a
  8087.         table.  The primary index is not counted.
  8088.  
  8089. Return Value     tindexes returns the number of secondary indexes on a
  8090.         table, or an error code if an error occurred.
  8091.  
  8092. See Also         tkeyed.
  8093.  
  8094.  
  8095.  
  8096. ----------------------------------------------------------------
  8097. tinsert
  8098. ----------------------------------------------------------------
  8099.  
  8100. Description      Insert record(s) into a table.
  8101.  
  8102. Declaration      int tinsert(TABLE *t, void *recs, int n);
  8103.  
  8104. Remarks        tinsert inserts n records into a table. The records
  8105.         are contained in the structure, or array of structures,
  8106.         pointed to by recs.  The structure pointed to by recs
  8107.         should exactly  mimic the structure of the table.  n
  8108.         should not be larger than the number of rows in the
  8109.         array.
  8110.  
  8111.         If t is keyed, then only those records which do not
  8112.         violate the primary key will be inserted.
  8113.  
  8114. Return Value     tinsert returns the number of rows inserted if
  8115.         successful or an error code if an error occurred.
  8116.  
  8117.  
  8118.  
  8119.                     - 132 -
  8120.  
  8121. See Also         tload, treplace.
  8122.  
  8123. Example          #include <stdio.h>
  8124.               #include "crde.h"
  8125.  
  8126.         int tbuffers = 128;
  8127.  
  8128.               typedef struct {
  8129.           char last[16], first[11], phone[9];
  8130.         } person;
  8131.  
  8132.               int main()
  8133.               {
  8134.           TABLE *t;
  8135.           person p;
  8136.           char c[10];
  8137.  
  8138.           /* create a simple phone number list */
  8139.           t = tcreat("phone", "last c16, first c11, phone c9");
  8140.  
  8141.           twritec(t);
  8142.           do {
  8143.             printf("\nlast name:  ");
  8144.             gets(p.last);
  8145.             printf("first name: ");
  8146.             gets(p.first);
  8147.             printf("phone no:   ");
  8148.             gets(p.phone);
  8149.  
  8150.             tinsert(t, &p, 1);
  8151.  
  8152.             printf("Enter another phone number (Y/N)?");
  8153.             gets(c);
  8154.           } while (toupper(*c) == 'Y');
  8155.  
  8156.           tclose(t);
  8157.         }
  8158.  
  8159.  
  8160.  
  8161. ----------------------------------------------------------------
  8162. tintersect
  8163. ----------------------------------------------------------------
  8164.  
  8165. Description      Returns the intersection of two tables.
  8166.  
  8167. Declaration      TABLE *tintersect(char *name, TABLE *t1, TABLE *t2);
  8168.  
  8169. Remarks          tintersect returns the relational intersection of
  8170.         tables t1 and t2. The result table will have a
  8171.         structure identical to t1 and contain all the
  8172.         records found in both t1 and t2.
  8173.  
  8174.  
  8175.  
  8176.  
  8177.  
  8178.  
  8179.                     - 133 -
  8180.  
  8181.         tintersect requires that t1 and t2 be compatible,
  8182.         i.e. that they have equivelent types and sizes on a
  8183.         column by column basis.  However they may have
  8184.         different primary or secondary keys.
  8185.  
  8186.         tintersect works differently depending upon whether
  8187.         t1 is keyed or not.  If t1 is keyed then tintersect
  8188.         will return every record in t1 whos key values matches
  8189.         the key values of any record in t2.  If t1 is not keyed,
  8190.         then only those records which entirely match a record
  8191.         in t2 will be included.
  8192.  
  8193.         If t1 or t2 is a TEMP table, then tintersect will drop
  8194.         either or both of them, respectively.  Otherwise, t1
  8195.         and t2 are unaffected by tintersect.
  8196.  
  8197. Return Value     tintersect returns a pointer to an open table if
  8198.         successful, or NULL if an error occurred.
  8199.  
  8200. See Also         tdiff, tunion.
  8201.  
  8202. Example        /* query which uses tintersect to find those parts
  8203.         ordered by both supplier #1 and supplier #2 */
  8204.  
  8205.         TABLE *answer;
  8206.  
  8207.         answer = tintersect(STATIC,
  8208.           tproject(TEMP, orders, "*p#", "s#", EQ, 1, NULL),
  8209.           tproject(TEMP, orders, "*p#", "s#", EQ, 2, NULL)
  8210.         );
  8211.  
  8212.  
  8213.  
  8214. ----------------------------------------------------------------
  8215. tjoin
  8216. ----------------------------------------------------------------
  8217.  
  8218. Description    Performs the relational join of two tables.
  8219.  
  8220. Declaration      TABLE *tjoin(char *name, TABLE *t1, char *cl1, TABLE
  8221.           *t2, char *cl2, char *pl);
  8222.  
  8223. Remarks          tjoin joins tables t1 and t2 creating a new table.
  8224.         The ability to join tables, relate information between
  8225.         tables, is at the heart of relational philosophy.
  8226.         Tables are related by "join" columns, tjoin works by
  8227.         connecting all the rows in t1 whose join columns have
  8228.         the same values as the corresponding join columns in t2.
  8229.  
  8230.         cl1 and cl2 are column lists describing the join
  8231.         columns for tables t1 and t2 respectively.  cl1 and
  8232.         cl2 must contain the same number of columns, and each
  8233.         must be of identical types on a column by column basis.
  8234.         Up to 256 join columns may be specified for each table.
  8235.  
  8236.  
  8237.  
  8238.  
  8239.                     - 134 -
  8240.  
  8241.         pl describes the result table.  pl is special column
  8242.         list which may include a primary key by prepending any
  8243.         columns with an asterisk '*'.  In addition, pl may
  8244.         contain columns from either t1 or t2.  However, if t1
  8245.         and t2 both contain a column of the same name, pl can
  8246.         only include one of them, since by definition every
  8247.         column name in a table must be unique.  By default,
  8248.         tjoin will chose the column in t1 over t2.  You should
  8249.         keep this in mind when designing tables that you may
  8250.         plan on joining later.
  8251.  
  8252.         If either t1 or t2 are TEMP tables, tjoin will drop
  8253.         either or both of them, respectively.  Otherwise, t1
  8254.         and t2 are unaffected by tjoin.
  8255.  
  8256. Return Value     tjoin returns a pointer to an open table if successful,
  8257.         or NULL if an error occurred.
  8258.  
  8259. Example        /* simple 2-table join containing all orders made by
  8260.         supplier #100 */
  8261.  
  8262.         TABLE *answer;
  8263.  
  8264.         answer = tjoin(STATIC,
  8265.           tselect(TEMP, sups, "s#", EQ, 100, NULL), "s#",
  8266.           orders, "s#",
  8267.           "s#,sname,city,status,p#,qty"
  8268.         );
  8269.  
  8270.  
  8271.  
  8272. ----------------------------------------------------------------
  8273. tkeyed
  8274. ----------------------------------------------------------------
  8275.  
  8276. Description      Determines whether a table is keyed or not.
  8277.  
  8278. Declaration      int tkeyed(TABLE *t);
  8279.  
  8280. Remarks         tkeyed returns 1 if t has a primary key, or 0 if not.
  8281.         tkeyed(t) + tindexes(t) returns the total number of
  8282.         indexes on a table.
  8283.  
  8284. Return Value    tkeyed return 1 if t is keyed or 0 if not. An error
  8285.         code is returned if an error occurred.
  8286.  
  8287. See Also         tindexes.
  8288.  
  8289. Example        #include <stdio.h>
  8290.         #include "crde.h"
  8291.  
  8292.         int tbuffers = 128;
  8293.  
  8294.  
  8295.  
  8296.  
  8297.  
  8298.  
  8299.                     - 135 -
  8300.  
  8301.         int main()
  8302.         {
  8303.           TABLE *t;
  8304.  
  8305.           t = topen("sups");
  8306.           if (tkeyed(t))
  8307.             printf("Sups table is keyed.\n");
  8308.           else
  8309.             printf("Sups table is not keyed.\n");
  8310.           tclose(t);
  8311.         }
  8312.  
  8313.  
  8314.  
  8315. ----------------------------------------------------------------
  8316. tkeys
  8317. ----------------------------------------------------------------
  8318.  
  8319. Description      Describes the secondary indexes on a table.
  8320.  
  8321. Declaration      TABLE *tkeys(char *name, TABLE *t);
  8322.  
  8323. Remarks        tkeys is a complementary function to tstruct.  It
  8324.         creates a table describing a table's secondary keys.
  8325.  
  8326.         tkeys creates a table equivelent to
  8327.  
  8328.             tcreat(name, "ext c4, key c144")
  8329.  
  8330.         and fills it with information about a table's
  8331.         secondary keys.  The ext column contains the filename
  8332.         extension of the index being described and is of the
  8333.         format "s[n]" where [n] is a digit from 0 to 7.  The
  8334.         key column contains the index descriptor which created
  8335.         the index.
  8336.  
  8337.         If t is a TEMP table, then it will be dropped
  8338.         automatically by tkeys.  Otherwise, t is unaffected by
  8339.         tkeys.
  8340.  
  8341. Return Value     tkeys returns a pointer to an open table if successful,
  8342.         or NULL if an error occurred.
  8343.  
  8344. See Also         tstruct.
  8345.  
  8346. Example:          #include <stdio.h>
  8347.               #include "crde.h"
  8348.  
  8349.         int tbuffers = 128;
  8350.  
  8351.               int main()
  8352.               {
  8353.           TABLE *t;
  8354.  
  8355.           /* Program to display information on all the
  8356.           secondary indexes on Sups */
  8357.  
  8358.  
  8359.                     - 136 -
  8360.  
  8361.           t = topen("sups");
  8362.  
  8363.           tview(tkeys(TEMP, t), NULL, NULL, 1, 1, 80, 25,
  8364.           0x07, 0x07, 0x07, NULL);
  8365.  
  8366.           tclose(t);
  8367.         }
  8368.  
  8369.  
  8370.  
  8371. ----------------------------------------------------------------
  8372. tload
  8373. ----------------------------------------------------------------
  8374.  
  8375. Description      Loads a record into a table column by column.
  8376.  
  8377. Declaration      int tload(TABLE *t, ...);
  8378.  
  8379. Remarks          tload expects a series of arguments, one for each
  8380.         column of the table.  Each argument must have the
  8381.         exact same type as the corresponding column in t.
  8382.         In the case of a char column, tload expects a char *
  8383.         to string which may be of any length.  tload will
  8384.         automatically truncate the string to fit in the
  8385.         appropriate column.  The resulting row is inserted
  8386.         into t.
  8387.  
  8388.         tload is useful when filling tables directly by the
  8389.         program without user input.
  8390.  
  8391. Return Value     tload returns 0 on success, or an error code if an
  8392.         error occurred.
  8393.  
  8394. See Also        tinsert, treplace.
  8395.  
  8396. Example          #include <stdio.h>
  8397.               #include "crde.h"
  8398.  
  8399.         int tbuffers = 128;
  8400.  
  8401.              int main()
  8402.         {
  8403.           TABLE *t;
  8404.  
  8405.           t = tcreat("customer", "*cust# , name c26, city c26,"
  8406.             " state c3, zip c6, balance $");
  8407.  
  8408.           tload(t, 1,             /* cust# */
  8409.             "Star Software",      /* name  */
  8410.             "Anywhere",           /* city  */
  8411.             "MI",                    /* state */
  8412.             "48262",                  /* zip   */
  8413.             0.0);                     /* balance */
  8414.  
  8415.  
  8416.  
  8417.  
  8418.  
  8419.                     - 137 -
  8420.  
  8421.           /* load some more records... */
  8422.           tload(t, 2, "Kent's Warehouse", "Jonesville", "NY",
  8423.             10079, 0.0);
  8424.           tload(t, 3, "The Grocery Store", "Auburn", "GA",
  8425.             72623, 0.0);
  8426.  
  8427.           tclose(t);
  8428.         }
  8429.  
  8430.  
  8431.  
  8432. ----------------------------------------------------------------
  8433. tlookup
  8434. ----------------------------------------------------------------
  8435.  
  8436. Description    Lookup rows in a table.
  8437.  
  8438. Declaration      int tlookup(TABLE *t, ...);
  8439.  
  8440. Remarks         tlookup is used to lookup values in a table.
  8441.         tlookup expects a series of arguments which form a
  8442.         search expression like the one described in tselect.
  8443.         tlookup will return 1 if it finds a record matching
  8444.         the criteria, or 0 if it does not.
  8445.  
  8446.         tlookup is useful for supporting lookup tables to
  8447.         validate entries into a database.  For example you
  8448.         might enter a customer number into an order table,
  8449.         then call tlookup to verify that that customer number
  8450.         exists in the customer table.
  8451.  
  8452.         tlookup is very similar to tscan.  The primary
  8453.         difference between the two is that tlookup stops
  8454.         after it finds the first record matching the criteria,
  8455.         while tscan attempts to find all records matching the
  8456.         criteria.  Subsequently, tlookup will generally be
  8457.         much faster than tscan.
  8458.  
  8459.         If t is a temporary table then it is automatically
  8460.         dropped by tlookup, even if an error occurs.  Otherwise,
  8461.         t is unaffected by tlookup.
  8462.  
  8463. Return Value     tlookup returns 1 if a matching record is found or 0
  8464.         if not.  It returns an error code if an error occurred.
  8465.  
  8466. See Also         tlookupif, tselect.
  8467.  
  8468. Example         #include <stdio.h>
  8469.         #include "crde.h"
  8470.  
  8471.         /* function to add an order to the order table - uses
  8472.         table lookup to validate the order */
  8473.  
  8474.               int AddOrder(order *o)
  8475.              {
  8476.           int i;
  8477.  
  8478.  
  8479.                     - 138 -
  8480.  
  8481.           /* validate new order against customer lookup table
  8482.           to insure o->custno is a valid customer.  Returns 1
  8483.           if successful. */
  8484.  
  8485.           i = tlookup(cust, "cust#", EQ, o->custno, NULL);
  8486.           if (i > 0)
  8487.             i = tinsert(orders, o, 1);
  8488.           return i;
  8489.         }
  8490.  
  8491.  
  8492.  
  8493. ----------------------------------------------------------------
  8494. tlookupif
  8495. ----------------------------------------------------------------
  8496.  
  8497. Description      Lookup rows in a table.
  8498.  
  8499. Declaration      int tlookupif(TABLE *t, int (*action)(void *), ...);
  8500.  
  8501. Remarks          tlookupif is exactly like tlookup except it accepts
  8502.         an additional parameter action.  Every row which
  8503.         matches the search expression is passed to action
  8504.         for further evaluation.  action should behave exactly
  8505.         as the action described in tselectif:
  8506.  
  8507.         Result of action    What happens
  8508.  
  8509.             > 0              Row found.  tlookupif returns 1.
  8510.  
  8511.             = 0              Row not found.  tlookupif keeps
  8512.                     looking.
  8513.  
  8514.             < 0             An error occurred. tlookupif
  8515.                     aborts and returns the result
  8516.                     of action.
  8517.  
  8518.               See tselectif for more details on action.
  8519.  
  8520.         If t is a temporary table then it is automatically
  8521.         dropped by tlookupif, even if an error occurs.
  8522.         Otherwise, t is unaffected by tlookupif.
  8523.  
  8524. Return Value     tlookupif returns 1 if a matching record is found or 0
  8525.         if not.  It returns an error code if an error occurred.
  8526.  
  8527. See Also         tlookup, tselect, tselectif.
  8528.  
  8529. Example        /* lookup function to determine if there are any red
  8530.         or blue parts */
  8531.  
  8532.         int RedOrBlue(void *rec)
  8533.         {
  8534.           return stricmp(((part *)rec)->color, "red") == 0 &&
  8535.             stricmp(((part *)rec)->color, "blue") == 0;
  8536.         }
  8537.  
  8538.  
  8539.                     - 139 -
  8540.  
  8541.         if (tlookupif(parts, RedOrBlue, NULL) > 0) {
  8542.           /* ... */
  8543.         }
  8544.  
  8545.  
  8546.  
  8547. ----------------------------------------------------------------
  8548. tmark
  8549. ----------------------------------------------------------------
  8550.  
  8551. Description      Determines whether a table is in a transaction.
  8552.  
  8553. Declaration      int tmark(TABLE *t);
  8554.  
  8555. Remarks          If t is in a transaction, tmark returns a value > 0.
  8556.         If t is not in a transaction, tmark returns 0.
  8557.  
  8558. Return Value     tmark returns > 0 if a transaction on the table is
  8559.         pending, 0 otherwise. An error code if returned if t
  8560.         is not a table.
  8561.  
  8562. See Also         tcommit, trollback, ttransact.
  8563.  
  8564. Example        #include <stdio.h>
  8565.         #include "crde.h"
  8566.  
  8567.         int tbuffers = 128;
  8568.  
  8569.         int VerifyTrans(TABLE *t)
  8570.         {
  8571.           if (tmark(t))
  8572.             printf("Table is in a transaction.\n");
  8573.           else
  8574.             printf("Table is not in a transaction.\n");
  8575.         }
  8576.  
  8577.         int main()
  8578.         {
  8579.           TABLE *t;
  8580.  
  8581.           t = topen("sups");
  8582.  
  8583.           VerifyTrans(t);
  8584.           ttransact(t);
  8585.           VerifyTrans(t);
  8586.           trollback(t);
  8587.           VerifyTrans(t);
  8588.  
  8589.           tclose(t);
  8590.         }
  8591.  
  8592.  
  8593.  
  8594.  
  8595.  
  8596.  
  8597.  
  8598.  
  8599.                     - 140 -
  8600.  
  8601. ----------------------------------------------------------------
  8602. tmax
  8603. ----------------------------------------------------------------
  8604.  
  8605. Description     Calculates the maximum value in a column.
  8606.  
  8607. Declaration     int tmax(TABLE *t, char *c, void *result);
  8608.  
  8609. Remarks          tmax calculates the maximum value in column c and
  8610.         places the result in result.  tmax works with all
  8611.         column types:  c, i, l, d, f, $.  The result type
  8612.         is identical to the type of the named column.
  8613.  
  8614.         result is undefined if an error occurs.
  8615.  
  8616.         If t is a TEMP table then it is automatically dropped
  8617.         by tmax, even if an error occurs.  Otherwise t is
  8618.         unaffected by tmax.
  8619.  
  8620. Return Value      tmin returns 0 if successful or an error code if an
  8621.         error occurred.
  8622.  
  8623. See Also    taverage, tcount, tmin, tsum.
  8624.  
  8625. Example        /* query to find all suppliers who have a status >=
  8626.         the supplier with the highest status in Atlanta */
  8627.  
  8628.         double d;
  8629.  
  8630.         tmax(tselect(TEMP, sups, "city", EQ, "Atlanta", NULL),
  8631.           "status", &d);
  8632.  
  8633.         answer = tselect(STATIC, sups, "status", GE, (int)d, NULL);
  8634.  
  8635.  
  8636.  
  8637. ----------------------------------------------------------------
  8638. tmin
  8639. ----------------------------------------------------------------
  8640.  
  8641. Description      Calculates the minimum value in a column.
  8642.  
  8643. Declaration      int tmin(TABLE *t, char *c, void *result);
  8644.  
  8645. Remarks          tmin calculates the minimum value in column c and
  8646.         places the result in result.  tmin works with all
  8647.         column types:  c, i, l, d, f, $.  The result type
  8648.         is identical to the type of the named column.
  8649.  
  8650.         result is undefined if an error occurs.
  8651.  
  8652.         If t is a TEMP table then it is automatically dropped
  8653.         by tmin, even if an error occurs.  Otherwise, t is
  8654.         unaffected by tmin.
  8655.  
  8656.  
  8657.  
  8658.  
  8659.                     - 141 -
  8660.  
  8661. Return Value     tmin returns 0 if successful or an error code if an
  8662.         error occurred.
  8663.  
  8664. See Also    taverage, tcount, tmax, tsum.
  8665.  
  8666. Example        /* query to find all suppliers who have a status <=
  8667.         the supplier with the lowest status in Atlanta */
  8668.  
  8669.         double d;
  8670.  
  8671.         tmin(tselect(TEMP, sups, "city", EQ, "Atlanta", NULL),
  8672.           "status", &d);
  8673.  
  8674.         answer = tselect(STATIC, sups, "status", LE, (int)d, NULL);
  8675.  
  8676.  
  8677.  
  8678. ----------------------------------------------------------------
  8679. tmode
  8680. ----------------------------------------------------------------
  8681.  
  8682. Description      Determines table mode.
  8683.  
  8684. Declaration     int tmode(TABLE *t);
  8685.  
  8686. Remarks     tmode returns 1 of 4 constants describing the current
  8687.         mode of the table:
  8688.  
  8689.         T_NORMAL    The table is in normal mode.
  8690.  
  8691.         T_WRITEC     The table is in write-caching mode.
  8692.  
  8693.         T_TEMP         The table is a TEMP temporary table.
  8694.  
  8695.         T_STATIC       The table is a STATIC temporary table.
  8696.  
  8697.         A permanent table will return either T_NORMAL or
  8698.         T_WRITEC.  A temporary table will return either T_TEMP
  8699.         or T_STATIC.
  8700.  
  8701. Return Value    tmode returns one of the constants above, or an error
  8702.         code if an error occurred.
  8703.  
  8704. See Also         tnormal, twritec.
  8705.  
  8706. Example        /* routine to display a table's current mode */
  8707.  
  8708.         int PrintMode(TABLE *t)
  8709.         {
  8710.           printf("Table mode = ");
  8711.           switch (tmode(t))
  8712.           {
  8713.             case T_NORMAL :
  8714.               printf("NORMAL");
  8715.               break;
  8716.  
  8717.  
  8718.  
  8719.                     - 142 -
  8720.  
  8721.             case T_WRITEC :
  8722.               printf("WRITEC");
  8723.               break;
  8724.             case T_STATIC :
  8725.               printf("STATIC");
  8726.               break;
  8727.             case T_TEMP :
  8728.               printf("TEMP");
  8729.               break;
  8730.           }
  8731.           printf(".\n");
  8732.         }
  8733.  
  8734.  
  8735.  
  8736. ----------------------------------------------------------------
  8737. tnormal
  8738. ----------------------------------------------------------------
  8739.  
  8740. Description      Puts a table in normal mode.
  8741.  
  8742. Declaration     int tnormal(TABLE *t);
  8743.  
  8744. Remarks        tnormal is turns off the write-caching mode set by
  8745.         twritec.  Any changes will be immediately written to
  8746.         disk after every CRDE function call.   Although this
  8747.         greatly improves data integrity, it can have dramatic
  8748.         effects upon performance.  twritec explains in more
  8749.         detail how CRDE buffering works and when you should
  8750.         switch between normal and writec modes.
  8751.  
  8752.         By default, all tables begin in normal mode (except
  8753.         temporary tables).
  8754.  
  8755.         tnormal has no effect on temporary tables (both TEMP
  8756.         and STATIC), tables in a transaction, or tables already
  8757.         in normal mode.
  8758.  
  8759. Return Value     tnormal returns 0 if successful, or an error code if
  8760.         an error occurred.
  8761.  
  8762. See Also        twritec.
  8763.  
  8764. Example        #include <stdio.h>
  8765.         #include "crde.h"
  8766.  
  8767.         typedef struct {
  8768.           int code;
  8769.           char des[64];
  8770.         } error_t;
  8771.  
  8772.         TABLE *err;
  8773.  
  8774.         void DisplayErr(void)
  8775.         {
  8776.           error_t e;
  8777.  
  8778.  
  8779.                     - 143 -
  8780.  
  8781.           if (terrno < 0) {
  8782.             if (tget(err, &e, 1, "code", EQ, terrno) > 0) {
  8783.               printf("Error %d: %s.\n", e.code, e.des);
  8784.             }
  8785.             else {
  8786.               printf("Error table fault.\n");
  8787.               exit(1);
  8788.             }
  8789.           }
  8790.         }
  8791.  
  8792.  
  8793.         void LoadErr(void)
  8794.         {
  8795.           /* create table containing error codes and messages */
  8796.  
  8797.           err = tcreat("error", "*code i, des c64");
  8798.           twritec(err);
  8799.           tload(err, -11, "Out of memory");
  8800.           tload(err, -16, "Integrity check");
  8801.           tload(err, -21, "Disk seek");
  8802.           /* load rest of error codes here... */
  8803.  
  8804.           tnormal(err);
  8805.         }
  8806.  
  8807.  
  8808.         int tbuffers = 128;
  8809.  
  8810.         int main()
  8811.         {
  8812.           TABLE *t;
  8813.  
  8814.           LoadErr();
  8815.  
  8816.           t = topen("sups");
  8817.           DisplayErr();
  8818.  
  8819.           tview(t, NULL, NULL, 3, 3, 78, 23, 0x07, 0x07, 0x07,
  8820.             NULL);
  8821.           DisplayErr();
  8822.  
  8823.           tall(tclose);
  8824.         }
  8825.  
  8826.  
  8827.  
  8828. ----------------------------------------------------------------
  8829. topen
  8830. ----------------------------------------------------------------
  8831.  
  8832. Description      Opens a table.
  8833.  
  8834. Declaration     TABLE *topen(char *name);
  8835.  
  8836.  
  8837.  
  8838.  
  8839.                     - 144 -
  8840.  
  8841. Remarks          topen opens the table specified by name and all
  8842.         associated indexes.  The table must already exist
  8843.         to be opened.
  8844.  
  8845.         topen does a number of integrity checks to insure
  8846.         that the table and/or any indexes are not corrupt
  8847.         topen will automatically attempt to repair any
  8848.         corrupted indexes on the table.  However, if the
  8849.         table itself is corrupt, you will have to use trepair
  8850.         to fix the table.
  8851.  
  8852.         topen will fail if it fails to open any of the files
  8853.         associated with the table.
  8854.  
  8855. Return Value     topen returns a pointer to an open table is successful,
  8856.         or NULL if an error occured.
  8857.  
  8858. See also         tclose.
  8859.  
  8860. Example          #include <stdio.h>
  8861.               #include "crde.h"
  8862.  
  8863.         int tbuffers = 128;
  8864.  
  8865.               int main()
  8866.               {
  8867.           TABLE *t;
  8868.  
  8869.           if ((t = topen("customer")) == NULL) {
  8870.             printf("unable to open table.\n");
  8871.             exit(1);
  8872.           }
  8873.           MainProgram();
  8874.         }
  8875.  
  8876.  
  8877.  
  8878. ----------------------------------------------------------------
  8879. tproject
  8880. ----------------------------------------------------------------
  8881.  
  8882. Description      Creates a relational projection of a table.
  8883.  
  8884. Declaration      TABLE *tproject(char *name, TABLE *t, char  *pl, ...);
  8885.  
  8886. Remarks          tproject creates a projection of t.  In relational
  8887.         terms, a projection of a table is a table containing
  8888.         one or more of the columns of the original.  pl is a
  8889.         column list which defines the projected table.  All
  8890.         the columns in pl must exist in t, although the
  8891.         reverse is not necessarily true.  In addition, the
  8892.         columns may be arranged in any order desired.
  8893.  
  8894.  
  8895.  
  8896.  
  8897.  
  8898.  
  8899.                     - 145 -
  8900.  
  8901.         You may also define a primary key on the projected
  8902.         table, which may be different from the original.  You
  8903.         can use this technique to extract distinct values in
  8904.         a column from a table.  For example, suppose you
  8905.         wanted a list of all distinct customer numbers in an
  8906.         orders table.  You  might use the following query:
  8907.  
  8908.               orders = topen("orders");
  8909.               answer = tproject(TEMP, orders, "*cust#", NULL);
  8910.  
  8911.         Adding the '*' in front of "cust#" removes any duplicate
  8912.         customer numbers from the answer table.
  8913.  
  8914.         tproject expects a series of arguments which form a
  8915.         search expression like the one described in tselect.
  8916.         With this you can project selected subsets of a table,
  8917.         or the whole table itself.
  8918.  
  8919.         If t is a temporary table then it is automatically
  8920.         dropped by tproject, even if an error occurs.  Otherwise,
  8921.         t is unaffected by tproject.
  8922.  
  8923. Return Value    tproject returns a pointer to an open table if
  8924.         successful, or NULL if an error occurred.
  8925.  
  8926. See Also         tprojectif, tselect.
  8927.  
  8928. Example        #include "crde.h"
  8929.  
  8930.         int main()
  8931.         {
  8932.           TABLE *t, *answer;
  8933.  
  8934.           /* Program to display all cities where there are
  8935.           suppliers, in alphabetical order */
  8936.  
  8937.           t = topen(sups);
  8938.  
  8939.           answer = tsort(STATIC,
  8940.             tproject(TEMP, sups, "*city", NULL),
  8941.             "city"
  8942.           );
  8943.  
  8944.           tview(answer, NULL, NULL, 3, 3, 78, 23, 0x07, 0x07,
  8945.             0x07, NULL);
  8946.  
  8947.           tall(tclose);
  8948.         }
  8949.  
  8950.  
  8951.  
  8952.  
  8953.  
  8954.  
  8955.  
  8956.  
  8957.  
  8958.  
  8959.                     - 146 -
  8960.  
  8961. ----------------------------------------------------------------
  8962. tprojectif
  8963. ----------------------------------------------------------------
  8964.  
  8965. Description      Creates a projection of a table.
  8966.  
  8967. Declaration      TABLE *tprojectif(char *name, TABLE *t,
  8968.           int (*action)(void *), ...);
  8969.  
  8970. Remarks          tprojectif is exactly like tproject except it accepts
  8971.         an additional parameter action.  Every row  which
  8972.         matches the search expression is passed to action for
  8973.         further evaluation.  action should behave exactly as
  8974.         the action described in tselectif:
  8975.  
  8976.               Result of action    What happens
  8977.         
  8978.                  > 0              Row is projected.
  8979.  
  8980.                  = 0              Row is not projected.
  8981.  
  8982.             < 0              An error occurred.  tprojectif
  8983.                     is aborted and returns NULL.
  8984.                     terrno is set to the result of
  8985.                     action.
  8986.  
  8987.         See tselectif for more details on action.
  8988.  
  8989.         If t is a TEMP table then it is automatically dropped
  8990.         by tprojectif, even if an error occurs.  Otherwise, t
  8991.         is unaffected by tprojectif.
  8992.  
  8993. Return Value     tprojectif returns a pointer to an open table if
  8994.         successful, or NULL if an error occurred.
  8995.  
  8996. See Also         tproject, tselect, tselectif.
  8997.  
  8998. Example        /* query to list all parts which are red or blue */
  8999.  
  9000.         int RedOrBlue(void *rec)
  9001.         {
  9002.           return stricmp(((part *)rec)->color, "red") == 0 &&
  9003.             stricmp(((part *)rec)->color, "blue") == 0;
  9004.         }
  9005.  
  9006.         answer = tprojectif(STATIC, parts, "*pname", RedOrBlue,
  9007.           NULL);
  9008.  
  9009.  
  9010.  
  9011.  
  9012.  
  9013.  
  9014.  
  9015.  
  9016.  
  9017.  
  9018.  
  9019.                     - 147 -
  9020.  
  9021. ----------------------------------------------------------------
  9022. trelease
  9023. ----------------------------------------------------------------
  9024.  
  9025. Description     Release memory buffers.
  9026.  
  9027. Declaration     int trelease(TABLE *t);
  9028.  
  9029. Remarks        trelease releases any memory buffers held by the table.
  9030.         Buffers which have not been written to disk (because of
  9031.         twritec or ttransact) are not released.  To release all
  9032.         memory allocated to a table:  end any transaction, flush
  9033.         the table and then call trelease.
  9034.  
  9035.         Since CRDE automatically  manages memory between all
  9036.         tables, it is never necessary to explicitly release a
  9037.         table's memory to help speed another operation.
  9038.         However, you can do so manually if you choose with
  9039.         trelease.
  9040.  
  9041.         tclose and tdrop both automatically release all memory
  9042.         allocated to the table as part of their  normal routine.
  9043.  
  9044. Return Value     trelease returns 0 if successful or an error code if an
  9045.         error occurred.
  9046.  
  9047. See Also         tflush, tnormal, twritec.
  9048.  
  9049.  
  9050.  
  9051. ----------------------------------------------------------------
  9052. trename
  9053. ----------------------------------------------------------------
  9054.  
  9055. Description      Renames a table.
  9056.  
  9057. Declaration      int trename(char *oldname, char *newname);
  9058.  
  9059. Remarks          trename renames a table and all associated indexes.
  9060.         A table should be closed before it is renamed.
  9061.         trename will fail if a table with the name new
  9062.         already exists.
  9063.  
  9064. Return Value     trename returns 0 if successful, or -1 if an error
  9065.         occured.
  9066.  
  9067. See Also         terase.
  9068.  
  9069. Example        #include "crde.h"
  9070.  
  9071.         int main()
  9072.         {
  9073.           /* renames "Sups" to "Supplier" */
  9074.           trename("sups", "supplier");
  9075.         }
  9076.  
  9077.  
  9078.  
  9079.                     - 148 -
  9080.  
  9081. ----------------------------------------------------------------
  9082. trepair
  9083. ----------------------------------------------------------------
  9084.  
  9085. Description     Repairs a corrupted table.
  9086.  
  9087. Declaration      TABLE *trepair(char *name, char *td);
  9088.  
  9089. Remarks         trepair attempts to a repair a corrupted table by
  9090.         using the table descriptor td as a model of what the
  9091.         table should look like.  It then trys to rebuild the
  9092.         table using the infomation provided.
  9093.  
  9094.         trepair makes all its assuptions based upon td.  The
  9095.         only assumption it makes about the table is that it
  9096.         is corrupted.  Thus, it is very important that td is
  9097.         correct. If it isn't, trepair could do more harm than
  9098.         good, most likely making the table totally unrecoverable.
  9099.  
  9100.         trepair can fix logically corrupted tables,
  9101.         specifically tables with corrupted headers, usually
  9102.         without a hitch.  trepair will attept to recover as
  9103.         many rows from the atble as it can, but there is no
  9104.         guarentee that all of them will be salvaged.  trepair
  9105.         can also fix certain types of physically damaged tables
  9106.         as well.  However, trepair is limited to the extent in
  9107.         which it can fix physically damaged tables.  It cannot
  9108.         undelete tables, nor can it repair lost clusters or
  9109.         sectors.  Also, trepair must be able to open the file
  9110.         to repair it.
  9111.  
  9112.         trepair repairs only the table and primary index, if
  9113.         one exists.  Any secondary indexes must be rebuilt
  9114.         manually. Simply call tindex to rebuild any secondary
  9115.         indexes.
  9116.  
  9117. Return Value     trepair returns a pointer to an open table if
  9118.         successful, or NULL if an error occurred.
  9119.  
  9120. Example        #include <stdio.h>
  9121.         #include "crde.h"
  9122.  
  9123.         int tbuffers = 128;
  9124.  
  9125.         int main()
  9126.         {
  9127.           TABLE *t;
  9128.  
  9129.           t = topen("sups");
  9130.           if (!t) {
  9131.             printf("Unable to open table, attempting to "
  9132.               "repair...\n");
  9133.             t = trepair("sups", "*s# i, sname c26, city c26, "
  9134.               "status i");
  9135.  
  9136.  
  9137.  
  9138.  
  9139.                     - 149 -
  9140.  
  9141.             if (!t) {
  9142.               printf("Repair failed.\n");
  9143.               exit(1);
  9144.             }
  9145.             else
  9146.               printf("Repair successful.  %ld rows "
  9147.             "recovered.\n", trowsfound);
  9148.           }
  9149.  
  9150.           MainProgram();
  9151.         }
  9152.  
  9153.  
  9154.  
  9155. ----------------------------------------------------------------
  9156. treplace
  9157. ----------------------------------------------------------------
  9158.  
  9159. Description      Replace a row in a table.
  9160.  
  9161. Declaration      long treplace(TABLE *t, void *recs, long n);
  9162.  
  9163. Remarks          treplace is identical to tinsert except that if a
  9164.         row with the same primary key already exists in the
  9165.         table, it is replaced by the new row.
  9166.  
  9167. Return Value     treplace returns the number of rows replaced if
  9168.         successful, or an error code if an error occurred.
  9169.  
  9170. See Also         tinsert.
  9171.  
  9172. Example        #include "crde.h"
  9173.  
  9174.         typedef struct {
  9175.           int s;
  9176.           char sname[26];
  9177.           char city[26];
  9178.           int status;
  9179.         } sup;
  9180.  
  9181.         sup buf[100];
  9182.  
  9183.         int tbuffers = 128;
  9184.  
  9185.         int main()
  9186.         {
  9187.           TABLE *t;
  9188.           int i, j;
  9189.  
  9190.           t = topen("sups");
  9191.           i = tget(t, sups, 100, "city", EQ, "Atlanta", NULL);
  9192.           DoOperationsOnBuf();
  9193.           treplace(t, sups, i);
  9194.           tclose(t);
  9195.         }
  9196.  
  9197.  
  9198.  
  9199.                     - 150 -
  9200.  
  9201. ----------------------------------------------------------------
  9202. trestruct
  9203. ----------------------------------------------------------------
  9204.  
  9205. Description      Restructures a table.
  9206.  
  9207. Declaration     TABLE *trestruct(char *name, TABLE *t, char *td);
  9208.  
  9209. Remarks     trestruct creates a restructured version of t.
  9210.         trestruct is similar to tproject in that you can
  9211.         remove columns, rearrange columns and redefine the
  9212.         primary key.  In addition you can also add new columns
  9213.         and change the types of columns within a table.
  9214.  
  9215.         td is a table descriptor like the one described in
  9216.         tcreat.  If the column listed in td is found in t
  9217.         then one of two things happens:  if the new column
  9218.         is of the same type then data from the original column
  9219.         is simply copied to the new column.  If the types are
  9220.         different, trestruct tries to convert the data to
  9221.         the new type.  The table listing valid type conversions
  9222.         is listed below:
  9223.  
  9224.               Type conversion table
  9225.  
  9226.                 c       i        l       d       f      $
  9227.  
  9228.         c      x       x       x       x    x       x
  9229.  
  9230.         i      x       x       x       -       x       x
  9231.  
  9232.         l      x       x       x       -       x       x
  9233.  
  9234.         d      x       -       -       x       -       -
  9235.  
  9236.         f      x       x       x       -       x       x
  9237.  
  9238.         $      x       x       x       -       x       x
  9239.  
  9240.  
  9241.               [x] can be converted      [-] cannot be converted
  9242.  
  9243.         If a column listed in td not found in t all the values in
  9244.         that column are initalized to some inital value (usually
  9245.         0 or "").
  9246.  
  9247.         Consider the following example:
  9248.  
  9249.         t = tcreat("dummy", "*id i, date d, quant i, amount f");
  9250.  
  9251.         r = trestruct("restruct", t,
  9252.           "*id c6, "    /* converted id from int to char */
  9253.           "quant i,"    /* switched date and... */
  9254.           "date d, "    /* ...quant columns     */
  9255.           "salary $");    /* new column--will be set to 0.00*/
  9256.  
  9257.           
  9258.  
  9259.                     - 151 -
  9260.  
  9261.         If t is a TEMP table then it is automatically dropped
  9262.         by trestruct, even if an error occurs.  Otherwise, t
  9263.         is unaffected by trestruct.
  9264.  
  9265. Return Value     trestruct returns a pointer to an open table if
  9266.         successful, or NULL if an error occurred.
  9267.  
  9268.  
  9269.  
  9270. ----------------------------------------------------------------
  9271. trollback
  9272. ----------------------------------------------------------------
  9273.  
  9274. Description      Rollback a transaction.
  9275.  
  9276. Declaration      int trollback(TABLE *t);
  9277.  
  9278. Remarks          trollback rolls back a transaction on a table
  9279.         returning the table to the state it was in just prior
  9280.         to beginning the transaction.
  9281.  
  9282.         trollback rolls back the table even if an error occurs.
  9283.         In either case, the transaction is considered over.
  9284.  
  9285.         Calling trollback outside a transaction has no effect
  9286.         on a table.
  9287.  
  9288. Return Value     trollback returns 0 if successful or an error code if
  9289.         an error occurred.
  9290.  
  9291. See Also         tcommit, ttransact.
  9292.  
  9293. Example        See tcommit.
  9294.  
  9295.  
  9296.  
  9297. ----------------------------------------------------------------
  9298. trows
  9299. ----------------------------------------------------------------
  9300.  
  9301. Description      Calculates number of rows in a table.
  9302.  
  9303. Declaration      long trows(TABLE *t);
  9304.  
  9305. Remarks        trows returns a number between 0 and 2147483647.
  9306.  
  9307. Return Value     trows returns the number of rows in the table or an
  9308.         error code.
  9309.  
  9310. See Also         tcols.
  9311.  
  9312. Example        /* generic routine to display a table with tview;
  9313.         table cannot be TEMP */
  9314.  
  9315.  
  9316.  
  9317.  
  9318.  
  9319.                     - 152 -
  9320.  
  9321.         int ViewTable(TABLE *t)
  9322.         {
  9323.           gotoxy(3, 2);
  9324.           printf("%ld rows found.", trows(t));
  9325.           return tview(t, NULL, NULL, 3, 3, 78, 23, 0x07, 0x07,
  9326.             0x07, NULL);
  9327.         }
  9328.  
  9329.  
  9330.  
  9331. ----------------------------------------------------------------
  9332. trowsize
  9333. ----------------------------------------------------------------
  9334.  
  9335. Description     Calculates size of a row/record in a table.
  9336.  
  9337. Declaration      int trowsize(TABLE *t);
  9338.  
  9339. Remarks          trowsize returns a value from 1 to 4000.
  9340.  
  9341. Return Value     trowsize returns the size of a single row if
  9342.         successful, or an error code if an error occurred.
  9343.  
  9344. See Also         tcolsize.
  9345.  
  9346. Example        #include <stdio.h>
  9347.         #include "crde.h"
  9348.  
  9349.         int tbuffers = 128;
  9350.  
  9351.         int main()
  9352.         {
  9353.           TABLE *t;
  9354.  
  9355.           t = topen("sups");
  9356.  
  9357.           printf("Sups table row = %d bytes in size.\n",
  9358.             trowsize(t));
  9359.  
  9360.           tclose(t);
  9361.         }
  9362.  
  9363.  
  9364.  
  9365. ----------------------------------------------------------------
  9366. tscan
  9367. ----------------------------------------------------------------
  9368.  
  9369. Description      Scans a table.
  9370.  
  9371. Declaration      long tscan(TABLE *t, ...);
  9372.  
  9373. Remarks        tscan expects a series of arguments which form a search
  9374.         expression like the one described in tselect.
  9375.  
  9376.  
  9377.  
  9378.  
  9379.                     - 153 -
  9380.  
  9381.         tscan doesn't really do much except count the number
  9382.         of rows which match the search expression. Generally,
  9383.         tscan has little use in most applications.
  9384.  
  9385. Return Value     tscan returns the number of rows scanned, or an error
  9386.         code if an error occurred.
  9387.  
  9388. See Also         tscanif, tselect.
  9389.  
  9390. Example        #include <stdio.h>
  9391.         #include "crde.h"
  9392.  
  9393.         int tbuffers = 128;
  9394.  
  9395.         int main()
  9396.         {
  9397.           TABLE *t;
  9398.           long result;
  9399.  
  9400.           t = topen("sups");
  9401.           result = tscan(t, "city", EQ, "Atlanta", NULL);
  9402.           printf("There are %ld suppliers in Atlanta.\n", result);
  9403.           tclose(t);
  9404.         }
  9405.             
  9406.  
  9407.  
  9408. ----------------------------------------------------------------
  9409. tscanif
  9410. ----------------------------------------------------------------
  9411.  
  9412. Description      Scans a table.
  9413.  
  9414. Declaration    long tscanif(TABLE *t, int (*action)(void *), ...);
  9415.  
  9416. Remarks          tscanif is exactly like tscan except it accepts an
  9417.         additional parameter action.  Every row which matches
  9418.         the search expression is passed to action for further
  9419.         evaluation.  action should behave exactly as the
  9420.         action described in tselectif.    However, tscanif, by
  9421.         definition, doesn't really do anything in itself.  It
  9422.         simply goes through all the rows matching the search
  9423.         expression and passes them to action.  Thus, in this
  9424.         case, it doesn't really matter whether action returns
  9425.         0 or > 0.
  9426.  
  9427.         Result of action    What happens
  9428.  
  9429.             > 0              Scanning continues.
  9430.  
  9431.             = 0              Scanning continues.
  9432.  
  9433.             < 0              An error occurred.  tscanif
  9434.                     is aborted and returns NULL.
  9435.                     terrno is set to the result of
  9436.                     action.
  9437.  
  9438.  
  9439.                     - 154 -
  9440.  
  9441.         See tselectif for more details on action.
  9442.  
  9443.         tscanif can be used for reporting.  Since action
  9444.         receives a copy of every record scanned, simply design
  9445.         an action function to print out the record.
  9446.  
  9447. Return Value     tscanif returns the number of records scanned, or an
  9448.         error code if an error occurred.
  9449.  
  9450. See Also         tscan, tselect, tselectif.
  9451.  
  9452. Example        See Chapter 12, Creating Reports.
  9453.  
  9454.  
  9455.  
  9456. ----------------------------------------------------------------
  9457. tselect
  9458. ----------------------------------------------------------------
  9459.  
  9460. Description      Selects rows/records from a table.
  9461.  
  9462. Declaration      TABLE *tselect(char *name, TABLE *t, ...);
  9463.  
  9464. Remarks          tselect creates a table, identical in structure t,
  9465.         containing records from t matching a specified search
  9466.         criteria.
  9467.  
  9468.         tselect expects a NULL terminated list of arguments
  9469.         which formulate the search expression.  A search
  9470.         expression contains a number of conditions.  Only
  9471.         those records which match all the conditions in the
  9472.         search expression are selected.
  9473.  
  9474.         A typical tselect function call will have the following
  9475.         format:
  9476.  
  9477.         TABLE *tselect(name, t, colname1, relop1, value1,
  9478.           colname2, relop2, value2, etc..., NULL);
  9479.  
  9480.         Each condition is a set of exactly three arguments:
  9481.         colname[n], relop[n], and value[n], described below...
  9482.  
  9483.         colname[n] is any valid column name as an asciiz
  9484.         string (char *).
  9485.  
  9486.         relop[n] is a relational operator (int) defined in
  9487.         crde.h.  Valid relops are:
  9488.  
  9489.         relop        C Equivelent
  9490.         
  9491.         EQ         ==
  9492.  
  9493.         LT                <
  9494.  
  9495.         GT                >
  9496.  
  9497.  
  9498.  
  9499.                     - 155 -
  9500.  
  9501.         LE,LTE            <=
  9502.  
  9503.         GE,GTE            >=
  9504.  
  9505.         NE,NEQ            !=
  9506.  
  9507.         value[n] is some value, either a constant or a
  9508.         variable.  The type of value[n] must be exactly the
  9509.         same as the column type of colname[n].  In the case
  9510.         of a char type, value[n] may be a char * pointing to
  9511.         a string of any length.
  9512.  
  9513.         An expression may contain no conditions, in whichcase,
  9514.         every record is selected.  Otherwise a row must match
  9515.         every condition to be selected.
  9516.  
  9517.         Here are some examples:
  9518.  
  9519.                   /* selects every record in table */
  9520.         tselect(TEMP, t, NULL);
  9521.  
  9522.                   /* selects every record where cust# == 131 */
  9523.         tselect(TEMP, t, "cust#", EQ, 131, NULL);
  9524.  
  9525.         /* selects every record where cust# == 131 && age > 21 */
  9526.         tselect(TEMP, t, "cust#", EQ, 131, "age", GT, 21, NULL);
  9527.  
  9528.         /* "qty" in this case is of type long - note hold the
  9529.         value must be cast as long */
  9530.         tselect(TEMP, t, "qty", GT, 1000L, NULL);
  9531.  
  9532.         /* select which uses variables */
  9533.         int drinkage = 21;
  9534.         ...
  9535.         tselect(TEMP, t, "name", EQ, "Mark", "age", GTE, drinkage,
  9536.           NULL);
  9537.  
  9538.         A search expression may not contain more than 16
  9539.         conditions.
  9540.  
  9541.         CRDE uses a powerful search engine to analyze and
  9542.         perform searches. The search engine knows about a
  9543.         table's columns and indexes and will automatically
  9544.         determine the fastest way to perform the search.  The
  9545.         search engine will use any indexes that are available,
  9546.         but can search the table sequentially as well.  In
  9547.         some cases it may opt for a sequential search, even
  9548.         if an available index exists, if it thinks it can
  9549.         perform the search faster that way.
  9550.  
  9551.         It also makes no difference in what order an expression
  9552.         is organized, the search engine always reorganizes an
  9553.         expression to produce the fastest possible search.
  9554.  
  9555.  
  9556.  
  9557.  
  9558.  
  9559.                     - 156 -
  9560.  
  9561.         Several other functions besides tselect utilize the
  9562.         search engine to perform conditional operations on a
  9563.         table.  These "search related functions" (excluding
  9564.         tselect) are:  tproject, tdelete, tchange, tscan,
  9565.         tlookup, and tget, respectively.  In addition, each
  9566.         search related function has a cousin function known as
  9567.         "..if functions", which provide even more powerful
  9568.         search capabilities including user-defined searches,
  9569.         nested queries, correlated sub-queries, and more.
  9570.         These are tselectif, tprojectif, tdeleteif, tchangeif,
  9571.         tscanif, tlookupif, and tgetif.  For more on the
  9572.         capabilities of "..if functions" see tselectif.
  9573.  
  9574.         If t is a TEMP table then it is dropped automatically
  9575.         by tselect, even if an error occurs.  Otherwise, t is
  9576.         unaffected by tselect.
  9577.  
  9578. Return Value    tselect returns a pointer to an open table if
  9579.         successful, of NULL if an error occurred.
  9580.  
  9581. See Also         tselectif, tproject, tdelete, tchange, tscan, tlookup,
  9582.         tget.
  9583.  
  9584.  
  9585.  
  9586. ----------------------------------------------------------------
  9587. tselectif
  9588. ----------------------------------------------------------------
  9589.  
  9590. Description      Selects rows/records from a table.
  9591.  
  9592. Declaration      TABLE *tselectif(char *name, TABLE *t,
  9593.           int (*action)(void *), ...);
  9594.  
  9595. Remarks          tselectif is one of seven "..if functions" which
  9596.         are identical to their regular counterparts in every
  9597.         respect except one: each accepts an additional
  9598.         parameter action.
  9599.  
  9600.         action is a user defined function which futher
  9601.         evaulates the row.  Every row which passes the initial
  9602.         search criteria is passed to action for further
  9603.         evaluation.
  9604.  
  9605.         action must return type int.  The result of action
  9606.         determines how the record should be processed:
  9607.  
  9608.               Result of action    Meaning
  9609.  
  9610.              > 0              Select the row.
  9611.  
  9612.                  = 0              Do not select the row.
  9613.  
  9614.  
  9615.  
  9616.  
  9617.  
  9618.  
  9619.                     - 157 -
  9620.  
  9621.             < 0              An error occurred.  tselectif
  9622.                     aborts and returns NULL.
  9623.                     terrno is set to the result of
  9624.                     action.
  9625.  
  9626.         action may modify the record passed to it without harm
  9627.         (although this does have side effects when used in
  9628.         tchangeif).  action can perform queries on other
  9629.         tables based on the information passed to it.  action
  9630.         can even perform queries on t, allowing for extremely
  9631.         powerful and complex querying capabilities.
  9632.  
  9633.         However, there is one restriction.  action is
  9634.         disallowed from altering t in any way.  When any
  9635.         "..if function" calls action, t is automatically placed
  9636.         in a special read-only mode.  Because of the complex
  9637.         nature in which CRDE performs searches, t cannot be
  9638.         changed during the course of a query.  CRDE functions
  9639.         which attempt to alter t will return a "table may not
  9640.         be modified" error.
  9641.  
  9642.         CRDE functions which can change a table are listed below:
  9643.  
  9644.         tadd          tchange        tchangeif     tchcol 
  9645.         tdelete        tdeleteif     tdropindex     tempty
  9646.         tindex        tinsert        treplace          tload
  9647.           tsubtract     tupdate
  9648.  
  9649.         In addition, you may not close, drop a table in read-
  9650.         only mode.  This is also the only case in which if t is
  9651.         a TEMP table, it will not be automatically dropped by
  9652.         any CRDE functions called in action.
  9653.  
  9654.         As you can see, "..if" functions provide almost
  9655.         unlimited querying capabilities.  You might think the
  9656.         these functions could replace their regular counterparts
  9657.         because you can do everything a search expression can
  9658.         do inside of action.
  9659.  
  9660.         However, there are several reasons why not to:
  9661.  
  9662.         - the most obvious is that "..if" function require you
  9663.         to write a user-defined function for every query.
  9664.  
  9665.         - CRDE can only take advantage your indexes if you
  9666.         specify a search expression.  For this reason you
  9667.         should always put as much of the query in the search
  9668.         expression as you possibly can.
  9669.  
  9670.         - and since action must receive a copy of every record
  9671.         for evaluation, "..if functions" are slightly slower
  9672.         than their regular counterparts.
  9673.  
  9674.  
  9675.  
  9676.  
  9677.  
  9678.  
  9679.                     - 158 -
  9680.  
  9681.         There are 7 "..if functions", each of which directly
  9682.         corresponds to one of the 7 "search related functions".
  9683.         All behave in a similar fashion as described above.
  9684.         They are: tprojectif, tdeleteif, tchangeif, tscanif,
  9685.         tlookupif, and tgetif, respectively.
  9686.  
  9687.         If t is a TEMP table then it is automatically dropped
  9688.         by tselectif, even if an error occurs.  Otherwise, t is
  9689.         unaffected by tselectif.
  9690.  
  9691. Return Value    tselectif returns a pointer to an open table if
  9692.         successful, or NULL if an error occurred.
  9693.  
  9694. See Also         tselect, tprojectif, tdeleteif, tchangeif, tscanif
  9695.         tlookupif, tgetif.
  9696.  
  9697. Example        /* query to select all suppliers who have a status >=
  9698.         the average status for suppliers from that city */
  9699.  
  9700.         int GTAvgForCity(void *rec)
  9701.         {
  9702.           int i;
  9703.           double d;
  9704.  
  9705.           i = taverage(tselect(TEMP, ((sup *)rec)->status, &d);
  9706.           if (i < 0)
  9707.             return i;
  9708.           return ((sup *)rec)->status >= (int)d;
  9709.         }
  9710.  
  9711.         answer = tselectif(STATIC, sups, GTAvgForCity, NULL);
  9712.  
  9713.  
  9714.  
  9715. ----------------------------------------------------------------
  9716. tsort
  9717. ----------------------------------------------------------------
  9718.  
  9719. Description      Sorts a table.
  9720.  
  9721. Declaration      TABLE *tsort(char *name, TABLE *t, char *id);
  9722.  
  9723. Remarks          tsort returns a copy of a table in sorted order.  The
  9724.         result table is physically sorted according to the sort
  9725.         order given by id.   id is any valid index descriptor
  9726.         (see tindex for a detailed description of index
  9727.         descriptors).  If t is a TEMP table then it is
  9728.         automatically dropped by tsort, even if an error
  9729.         occurs.  Otherwise, t is unaffected by tsort.
  9730.  
  9731. Return Value    tsort returns a pointer to an open table is successful,
  9732.         or NULL if an error occurred.
  9733.  
  9734. See Also         tindex.
  9735.  
  9736.  
  9737.  
  9738.  
  9739.                     - 159 -
  9740.  
  9741. Example          #include <stdio.h>
  9742.               #include "crde.h"
  9743.  
  9744.         int tbuffers = 128;
  9745.  
  9746.               int main()
  9747.               {
  9748.           TABLE *t;
  9749.  
  9750.           /* Display all suppliers with a status <= 5 ordered
  9751.           by supplier name */
  9752.  
  9753.           t = topen("sups");
  9754.  
  9755.           answer = tsort(STATIC,
  9756.             tselect(TEMP, t, "status", LE, 5, NULL),
  9757.             "sname"
  9758.           );
  9759.  
  9760.           tview(answer, NULL, NULL, 3, 3, 78, 23, 0x07, 0x07,
  9761.             0x07, NULL);
  9762.  
  9763.           tall(tclose);
  9764.         }
  9765.  
  9766.  
  9767.  
  9768. ----------------------------------------------------------------
  9769. tstruct
  9770. ----------------------------------------------------------------
  9771.  
  9772. Description      View the structure of a table.
  9773.  
  9774. Declaration      TABLE *tstruct(char *name, TABLE *t);
  9775.  
  9776. Remarks          tstruct returns the structure of a table as a table.
  9777.         It is useful for looking at table's structure if you
  9778.         forget the columns or primary key of a table.  A
  9779.         similar routine exists for examining the secondary
  9780.         keys on a table:  tkeys.
  9781.  
  9782.         The format of the struct table is equivelent to
  9783.  
  9784.             tcreat(name, "name c18, type c6"); 
  9785.  
  9786.         The rows in the struct table represent the columns in
  9787.         the table.  The name column contains the column name
  9788.         (preceded by an '*' if part of the primary key.) The
  9789.         type column contains the type specifier as would be
  9790.         specified in a table descriptor.
  9791.  
  9792.         If t is a temporary table then it is automatically
  9793.         dropped by tstruct, even if an error occurs.
  9794.         Otherwise,t is unaffected by tstruct.  In addition,
  9795.         altering the struct table has no effect t.
  9796.  
  9797.  
  9798.  
  9799.                     - 160 -
  9800.  
  9801. Return Value     tstruct returns a pointer to an open table if
  9802.         successful, of NULL if an error occurred.
  9803.  
  9804. See Also         tkeys.
  9805.  
  9806. Example         #include <stdio.h>
  9807.              #include "crde.h"
  9808.  
  9809.         int tbuffers = 128;
  9810.  
  9811.               int main()
  9812.              {
  9813.           TABLE *t;
  9814.  
  9815.           /* command line progam to view the structure of any
  9816.           table. */
  9817.  
  9818.           if (argc < 2) {
  9819.             printf("usage: tstruct <tablename>\n");
  9820.             exit(1);
  9821.           }
  9822.  
  9823.           if ((t = topen(argv[1])) != NULL)  {
  9824.             tview(tstruct(TEMP, t), NULL, NULL, 3, 3, 78, 23,
  9825.               0x07, 0x07, 0x07, NULL);
  9826.             tclose(t);
  9827.           }
  9828.           else
  9829.             printf("unable to open table.\n");
  9830.         }
  9831.  
  9832.  
  9833.  
  9834. ----------------------------------------------------------------
  9835. tsubtract
  9836. ----------------------------------------------------------------
  9837.  
  9838. Description      Removes records from one table which exist in another.
  9839.  
  9840. Declaration      long tsubtract(TABLE *t1, TABLE *t2);
  9841.  
  9842. Remarks          tsubtract removes records in one table which exist in
  9843.         another.  t1 and t2  must be compatible tables (see
  9844.         tunion for a description of compatible tables). t2 is
  9845.         unaffected by tsubtract.
  9846.  
  9847.         How tsubtract works depends upon whether t1 is keyed
  9848.         or not.  If t1 is keyed then it will lose all rows
  9849.         whose key values matches the key values of any record
  9850.         in t2.  If t1 is not keyed then it will lose any record
  9851.         which exactly matches any record in t2.
  9852.  
  9853.         If t2 is a TEMP table then it is automatically dropped
  9854.         by tsubstract, even if an error occurs.  Otherwise, t2
  9855.         is unaffected by tsubtract.
  9856.  
  9857.  
  9858.  
  9859.                     - 161 -
  9860.  
  9861. Return Value    tsubtract returns the number of rows subtracted if
  9862.         successful, or an error code if an error occurred.
  9863.  
  9864. See Also         tadd, tupdate.
  9865.  
  9866.  
  9867.  
  9868. ----------------------------------------------------------------
  9869. tsum
  9870. ----------------------------------------------------------------
  9871.  
  9872. Description      Calculate the sum of a column.
  9873.  
  9874. Declaration      int tsum(TABLE *t, char *c, void *result);
  9875.  
  9876. Remarks          tsum calculates the sum of column c in a table and
  9877.         places the result in result.  The column must be
  9878.         column must be of type i, l, f, or $.  Other types
  9879.         are ignored.  The result is always assumed to be of
  9880.         type double.
  9881.  
  9882.         If t is a TEMP table then it is automatically dropped
  9883.         by tsum, even if an error occurs.  Otherwise, t is
  9884.         unaffected by tsum.
  9885.  
  9886. Return Value     tsum returns 0 if successful or an error code if an
  9887.         error occurred.
  9888.  
  9889. See Also         taverage, tcount, tmax, tmin, tmax.
  9890.  
  9891. Example         #include <stdio.h>
  9892.         #include "crde.h"
  9893.  
  9894.         int tbuffers = 128;
  9895.  
  9896.         int main()
  9897.         {
  9898.           TABLE *t;
  9899.           double d;
  9900.  
  9901.           /* display total number of parts ordered by a supplier */
  9902.  
  9903.           t = topen("orders");
  9904.           tsum(tselect(TEMP, t, "s#", EQ, 100, NULL), "qty", &d);
  9905.           printf("Total number of parts ordered by supplier "
  9906.             "#100 = %ld.\n", (long)d);
  9907.           tclose(t);
  9908.         }
  9909.  
  9910.  
  9911.  
  9912.  
  9913.  
  9914.  
  9915.  
  9916.  
  9917.  
  9918.  
  9919.                     - 162 -
  9920.  
  9921. ----------------------------------------------------------------
  9922. ttransact
  9923. ----------------------------------------------------------------
  9924.  
  9925. Description      Begins a transaction.
  9926.  
  9927. Declaration      int ttransact(TABLE *t);
  9928.  
  9929. Remarks         ttransact begins a transaction on the table.  The
  9930.         table and all indexes are included in the transaction
  9931.         so you may continue to use the table just as you
  9932.         would normally.
  9933.  
  9934.         Any changes made to the table are recorded in memory.
  9935.         The transaction contines until a call to trollback,
  9936.         tcommit, tclose, or tdrop is made.  A call to tcommit
  9937.         or tclose will save all the changes made to the table.
  9938.         trollback or tdrop, on the other hand, discards the
  9939.         changes, returning the table back to the state it was
  9940.         in just prior to beginning the transaction.
  9941.  
  9942.         ttransact automatically flushes all buffers in t before
  9943.         starting the transaction to maximize integrity.
  9944.  
  9945.         Because CRDE keeps all transactions in memory, you may
  9946.         experience an -11 "Out of memory" error while performing
  9947.         transactions on very large tables.  If this occurs, you
  9948.         should immediately rollback the transaction before doing
  9949.         anything else.  However, even in the event of an error,
  9950.         trollback will be able to restore the original table.
  9951.  
  9952.         Calling ttransact while in a transaction has no effect
  9953.         on the table.
  9954.  
  9955. Return Value     ttransact returns 0 if succesful, or an error code if
  9956.         an error occurred.
  9957.  
  9958. See Also         tcommit, trollback.
  9959.  
  9960. Example        See tcommit.
  9961.  
  9962.  
  9963.  
  9964. ----------------------------------------------------------------
  9965. tupdate
  9966. ----------------------------------------------------------------
  9967.  
  9968. Description      Update records in one table with those from another.
  9969.  
  9970. Declaration      long tupdate(TABLE *t1, TABLE *t2);
  9971.  
  9972. Remarks          tupdate is similar to tadd except that records with
  9973.         identical primary keys are replaced by those in t2.
  9974.         If t1 is an unkeyed table, tupdate behaves exactly
  9975.         like tadd.
  9976.  
  9977.  
  9978.  
  9979.                     - 163 -
  9980.  
  9981.         t1 and t2 must be compatible tables, i.e. identical
  9982.         on a column by column basis in both type and size.
  9983.         They may have different primary and secondary keys
  9984.         however.
  9985.  
  9986.         If t is a TEMP table then it is automatically dropped
  9987.         by tupdate, even if an error occurs.  Otherwise, t is
  9988.         unaffected by tupdate.
  9989.  
  9990. Return Value     tupdate returns the number of rows updated if successful,
  9991.         or an error code if an error occurred.
  9992.  
  9993. See Also        tadd, tsubtract.
  9994.  
  9995. Example        #include <stdio.h>
  9996.         #include "crde.h"
  9997.  
  9998.         int tbuffers = 128;
  9999.  
  10000.         int main()
  10001.         {
  10002.           TABLE *sups, *recv;
  10003.  
  10004.           /* update the Sups table with an ascii file containing
  10005.           updates received from the home office */
  10006.  
  10007.           sups = topen("sups");
  10008.           recv = timportascii(STATIC, "*s# i, sname c26, city "
  10009.             "c26, status i", "recv.dat");
  10010.  
  10011.           tupdate(sups, recv);
  10012.  
  10013.           tclose(sups);
  10014.           tclose(recv);
  10015.         }
  10016.  
  10017.  
  10018.  
  10019. ----------------------------------------------------------------
  10020. tunion
  10021. ----------------------------------------------------------------
  10022.  
  10023. Description    Returns the union of two tables.
  10024.  
  10025. Declaration      TABLE *tunion(TABLE *t1, TABLE *t2);
  10026.  
  10027. Remarks          tunion returns the relational union of tables t1
  10028.         and t2.  t1 and t2 must be compatible, i.e.
  10029.         identical on a column by column basis in both type
  10030.         and size.  They do not have to have identical
  10031.         primary or secondary keys.
  10032.  
  10033.         The result of tunion is dependent upon whether t1
  10034.         is keyed or not.  tunion works by simply creating a
  10035.         copy of t1 and subsequently adding to it the records
  10036.         from t2.  If t1 is keyed, only those records from
  10037.  
  10038.  
  10039.                     - 164 -
  10040.  
  10041.         t2 which do not have a matching key in t1 will be
  10042.         added to the result. If t1 is not keyed, the result
  10043.         will contain all the records from t1 and t2.
  10044.  
  10045.         tunion is useful for ORing queries together.  For
  10046.         example, suppose you wanted a list of all customers
  10047.         whose age > 50 or salary < 5000.00.  You could
  10048.         construct the query as
  10049.  
  10050.               cust = topen("customer");
  10051.         answer = tunion(TEMP,
  10052.           tselect(TEMP, cust, "age", GT, 50, NULL),
  10053.           tselect(TEMP, cust, "salary", LT, 5000.00, NULL)
  10054.         );
  10055.  
  10056.         Note that this query will only work correctly if cust
  10057.         has a primary key.  Otherwise, the answer table may
  10058.         contain duplicates (if the are any customers who are
  10059.         both older then 50 and make less than 5000.00).
  10060.  
  10061.         If t1 or t2 is a temporary table then either, or both,
  10062.         are automatically dropped by tunion, even if an error
  10063.         occurs.  Otherwise, both t1 and t2 are unaffected by
  10064.         tunion.
  10065.  
  10066. Return Value     tunion returns a pointer to an open table is successful,
  10067.         or NULL if an error occurred.
  10068.  
  10069. See Also         tdiff, tintersect.
  10070.  
  10071.  
  10072.  
  10073. ----------------------------------------------------------------
  10074. tview
  10075. ----------------------------------------------------------------
  10076.  
  10077. Description     Views a table.
  10078.  
  10079. Declaration     int tview(TABLE *t, char *cl, char *id, int x1, int y1,
  10080.           int x2, int y2, int cattr, int fattr, int battr,
  10081.           void *rec);
  10082.  
  10083. Remarks          tview allows you to integrate powerful browse
  10084.         capabilities into your programs.  With tview you can
  10085.         view any table in any window on your screen, displaying
  10086.         only those columns you want in the order that you want.
  10087.         You can even use tview to select rows from a table.
  10088.  
  10089.         cl    Column list determining which columns to be
  10090.             displayed and which order they are to be
  10091.             displayed in.  If cl is NULL, then all columns
  10092.             are displayed in the order in which they were
  10093.             originally defined in the table.
  10094.  
  10095.  
  10096.  
  10097.  
  10098.  
  10099.                     - 165 -
  10100.  
  10101.         id      Index descriptor which describes the sort order
  10102.             the rows are to appear in.  id may be any legal
  10103.             index descriptor.  If id is NULL, then the rows
  10104.             are displayed in the order that they were
  10105.             originally inserted into the table.
  10106.  
  10107.               x1, y1, x2, y2
  10108.             Coordinates of the window to display the view
  10109.             in.  May be any window in 1,1 to 80,25 but must
  10110.             have a minimum height of 1 and a minimum width
  10111.             of 5.  tview automatically adjusts itself to
  10112.             the size of the defined window, allowing you to
  10113.             scroll both horizontally and vertically through
  10114.             the table.
  10115.  
  10116.         cattr      text attribute of column values
  10117.         fattr     text attribute of the view frame
  10118.         battr      text attribute for background (in window)
  10119.  
  10120.         rec     When tview returns, a copy of the rows occupied
  10121.             by the cursor is copied into the buffer pointed
  10122.             to by rec.  The buffer must be large enough to
  10123.             hold the record.  If rec is NULL, then the
  10124.             record is not copied.
  10125.  
  10126.               Cursor control in tview:
  10127.  
  10128.               Up             Go up a row.
  10129.               Down           Go down a row.
  10130.               Left           Go to the next column to the left.
  10131.               Right          Go to the next column to the right.
  10132.               Ctrl-Left      Go to the first column
  10133.               Ctrl-Right     Go to the last column
  10134.               PgUp          Go up a page of rows.
  10135.               PgDn           Go down a page of rows.
  10136.               Home           Go to the first row.
  10137.               End            Go to the last row.
  10138.  
  10139.               Enter          Exit returning decimal code 13.
  10140.               Esc            Exit returning decimal code 27.
  10141.  
  10142.         If t is a TEMP table then it is automatically dropped
  10143.         by tview, even if an error occurs.  Otherwise t is
  10144.         unaffected by tview.
  10145.  
  10146.               tview adds about 10k to your programs.
  10147.  
  10148. Return Value     tview returns 13 or 27 depending upon whether the
  10149.         user exits by striking Enter or Esc respectively.
  10150.         An error code is returned if an error occurred.
  10151.  
  10152. Example          #include <stdio.h>
  10153.              #include "crde.h"
  10154.  
  10155.         int tbuffers = 128;
  10156.  
  10157.  
  10158.  
  10159.                     - 166 -
  10160.  
  10161.         int main()
  10162.         {
  10163.           TABLE *t;
  10164.  
  10165.           /* view Sups table with columns reversed */
  10166.           t = topen("sups");
  10167.  
  10168.           tview(t,"status,city,sname,s#", NULL, 3, 3, 78, 23,
  10169.             0x07, 0x07, 0x07, NULL);
  10170.  
  10171.           tclose(t);
  10172.         }
  10173.  
  10174.  
  10175.  
  10176. ----------------------------------------------------------------
  10177. twritec
  10178. ----------------------------------------------------------------
  10179.  
  10180. Description     Puts a table in write-cache mode.
  10181.  
  10182. Declaration      int twritec(TABLE *t);
  10183.  
  10184. Remarks          twritec causes CRDE to buffer as many changes to a
  10185.         table as it can before flushing them to disk.
  10186.         Normally, CRDE writes all changes immediately to
  10187.         disk after every function call.  However twritec
  10188.         will prevent this, forcing CRDE to buffer as many
  10189.         changes as possible in memory before writing them
  10190.         to disk.
  10191.  
  10192.         twritec is useful when making a bunch of changes at
  10193.         once.  For example, a program might call twritec
  10194.         before inserting a number of records into a table,
  10195.         and then call tnormal when the operation is complete.
  10196.         Using this technique can have dramatic affects upon
  10197.         performance.
  10198.  
  10199.         On the other hand, doing something like:
  10200.  
  10201.         twritec(t);
  10202.         tdelete(t, "qty", LT 10, NULL);
  10203.         tnormal(t);
  10204.  
  10205.         won't provide any performance boost.  Why?, becuase
  10206.         all CRDE functions do internal buffering for the
  10207.         duration of the command.  twritec or tnormal only
  10208.         affect the way CRDE handles buffering after the
  10209.         function is completed.
  10210.  
  10211.         You can flush unwritten buffers at any time with
  10212.         tflush.  During the course of a program's execution,
  10213.         CRDE may flush them as well.  In either case, CRDE
  10214.         will continue to buffer changes until tnormal is
  10215.         called, or the table is closed or dropped.
  10216.  
  10217.  
  10218.  
  10219.                     - 167 -
  10220.  
  10221.         twritec has no effect on temporary tables, tables in
  10222.         a transaction, or tables already in writec mode.
  10223.  
  10224. Return Value     twritec return 0 if successful, or an error code if
  10225.         an error occurred.
  10226.  
  10227. See Also         tflush, tnormal.
  10228.  
  10229. Example          #include <stdio.h>
  10230.               #include "crde.h"
  10231.  
  10232.         int tbuffers = 128;
  10233.  
  10234.               int main()
  10235.               {
  10236.           TABLE *t;
  10237.  
  10238.           t = tcreat("class", "*studentid i, last c16, init c2, "
  10239.             "gpa f");
  10240.  
  10241.           twritec(t);  /* cache disk writes... */
  10242.  
  10243.           /* load the table with some students */
  10244.           tload(1522, "Adams", "D", 3.42);
  10245.           tload(1627, "Burns", "C", 3.92);
  10246.           tload(817 , "Bellamy", "J", 2.98);
  10247.  
  10248.           /*
  10249.             load rest of students here...
  10250.           */
  10251.  
  10252.           tclose(t);  /* unnecessary to call tnormal, tclose
  10253.                   automatically flushes buffers before
  10254.                   closing the table. */
  10255.         }
  10256.  
  10257.  
  10258.  
  10259. ----------------------------------------------------------------
  10260. year
  10261. ----------------------------------------------------------------
  10262.  
  10263. Description    Return the calendar year of a date_t value.
  10264.  
  10265. Declaration    int year(date_t d);
  10266.  
  10267. Remarks        year returns either 365 or 366 depending upon whether
  10268.         the date is in a leap year or not.
  10269.  
  10270. Return Value    year returns the calender year of the date, or -1 if
  10271.         the date is invalid.
  10272.  
  10273. See Also    day, month.
  10274.  
  10275. Example        See day.
  10276.  
  10277.  
  10278.  
  10279.                     - 168 -
  10280.  
  10281. ----------------------------------------------------------------
  10282. yearadd
  10283. ----------------------------------------------------------------
  10284.  
  10285. Description    Performs year addition/subtraction on a date.
  10286.  
  10287. Declaration    date_t yearadd(date_t d, int years);
  10288.  
  10289. Remarks        yearadd adds years years to d creating a new date.
  10290.         yearadd knows about leap years and correctly performs
  10291.         year addition so
  10292.  
  10293.             yearadd(mkdate(1, 1, 1992), 1)
  10294.  
  10295.         correctly yields 1/1/1993.
  10296.  
  10297.         You can subtract years from the date by placing a
  10298.         negative value for years.
  10299.  
  10300. Return Value    yearadd returns a date_t value or -1 if the date is
  10301.         invalid.
  10302.  
  10303. See Also    monthadd.
  10304.  
  10305.  
  10306.  
  10307.  
  10308.  
  10309.  
  10310.  
  10311.  
  10312.  
  10313.  
  10314.  
  10315.  
  10316.  
  10317.  
  10318.  
  10319.  
  10320.  
  10321.  
  10322.  
  10323.  
  10324.  
  10325.  
  10326.  
  10327.  
  10328.  
  10329.  
  10330.  
  10331.  
  10332.  
  10333.  
  10334.  
  10335.  
  10336.  
  10337.  
  10338.  
  10339.                     - 169 -
  10340.  
  10341.  
  10342.  
  10343.  
  10344.  
  10345.  
  10346. Global Variables
  10347.  
  10348.  
  10349.  
  10350. ----------------------------------------------------------------
  10351. tbuffers
  10352. ----------------------------------------------------------------
  10353.  
  10354. Description    Number of buffers to allocate to CRDE.
  10355.  
  10356. Declaration    int tbuffers;
  10357.  
  10358. Remarks        Every program which uses CRDE must declare this
  10359.         variable globally in order for CRDE to run.  CRDE
  10360.         uses the value of this variable to allocate the
  10361.         memory buffers it will use to perform its operations
  10362.         dynamically at run time.
  10363.  
  10364.         Each buffer is 512 bytes in size.  CRDE requires an
  10365.         absolute minimum of 64 buffers (32 k) to run.  The
  10366.         more buffers CRDE has available to it the faster it
  10367.         will run.  Normally a value of 128 will suffice for
  10368.         most applications.  However, programs which use large
  10369.         tables, complex queries, and/or transaction tracking
  10370.         may require 256, 512, or more buffers to run
  10371.         effectively.
  10372.  
  10373. Example        Normally tbuffers is delcared something like this:
  10374.  
  10375.         int tbuffers = 128;   /* standard min setting */
  10376.  
  10377.         int main()
  10378.         {
  10379.           /* program here */
  10380.         }
  10381.  
  10382.  
  10383.  
  10384. ----------------------------------------------------------------
  10385. terrno
  10386. ----------------------------------------------------------------
  10387.  
  10388. Description      Holds error code of the last occurring error.
  10389.  
  10390. Declaration      int terrno;
  10391.  
  10392. Remarks          terrno holds the error code of the last error that
  10393.         occurred.  terrno is set whenever an error occurs,
  10394.         but is never cleared by any CRDE function.  However,
  10395.         you may clear it if you wish.  Thus, terrno is only
  10396.         valid for the current function if the function itself
  10397.  
  10398.  
  10399.                     - 170 -
  10400.  
  10401.         returns an error (either an error code, NULL, or
  10402.         in rare cases 0, depending upon the function).  If a
  10403.         function returns an error code, then terrno will be
  10404.         set to the same error code.  If a function returns
  10405.         NULL or 0 for an error you can use terrno to determine
  10406.         the cause of the error.
  10407.  
  10408. Example        #include <stdio.h>
  10409.         #include "crde.h"
  10410.  
  10411.         int tbuffers = 128;
  10412.  
  10413.         int main()
  10414.         {
  10415.           TABLE *t;
  10416.  
  10417.           t = topen("sups");
  10418.  
  10419.           if (!t) {
  10420.             printf("Unable to open table (error code = %d).\n",
  10421.               terrno);
  10422.             exit(1);
  10423.           }
  10424.  
  10425.           RestOfProgram();
  10426.  
  10427.           tclose(t);
  10428.         }
  10429.  
  10430.  
  10431.  
  10432. ----------------------------------------------------------------
  10433. trowsfound
  10434. ----------------------------------------------------------------
  10435.  
  10436. Description      Number of rows involved in last operation.
  10437.  
  10438. Declaration     long trowsfound;
  10439.  
  10440. Remarks          trowsfound holds the number of records changed, deleted,
  10441.         imported, etc., from the last operation.  trowsfound is
  10442.         set by most CRDE functions.
  10443.  
  10444.         In the event of an error, trowsfound can be used to
  10445.         detemine the number of rows processed before the error
  10446.         occurred.
  10447.  
  10448. Example        #include <stdio.h>
  10449.         #include "crde.h"
  10450.  
  10451.         int tbuffers = 128;
  10452.  
  10453.         int main()
  10454.         {
  10455.           TABLE *t;
  10456.  
  10457.  
  10458.  
  10459.                     - 171 -
  10460.  
  10461.           t = topen("sups");
  10462.  
  10463.           result = texportascii(t, "sups.dat");
  10464.           if (result < 0)
  10465.             printf("Export failed (error code = %d) after "
  10466.               "%ld rows.\n", terrno, trowsfound);
  10467.           else
  10468.             printf("Export successful.  &ld rows exported.\n",
  10469.               trowsfound);
  10470.  
  10471.           tclose(t);
  10472.         }
  10473.  
  10474.  
  10475.  
  10476.  
  10477.  
  10478.  
  10479.  
  10480.  
  10481.  
  10482.  
  10483.  
  10484.  
  10485.  
  10486.  
  10487.  
  10488.  
  10489.  
  10490.  
  10491.  
  10492.  
  10493.  
  10494.  
  10495.  
  10496.  
  10497.  
  10498.  
  10499.  
  10500.  
  10501.  
  10502.  
  10503.  
  10504.  
  10505.  
  10506.  
  10507.  
  10508.  
  10509.  
  10510.  
  10511.  
  10512.  
  10513.  
  10514.  
  10515.  
  10516.  
  10517.  
  10518.  
  10519.                     - 172 -
  10520.  
  10521.  
  10522.  
  10523.  
  10524.  
  10525.  
  10526. Appendix A
  10527.  
  10528. Error Codes
  10529.  
  10530.  
  10531.  
  10532. General Errors
  10533.  
  10534.  
  10535. -1    General Error
  10536.  
  10537.     -1 is used as a general error code by CRDE.  The actual meaning
  10538.     of the error is completely context dependent.  CRDE may also
  10539.     generate a -1 code if it cannot determine the cause of an error.
  10540.     That situation, however, is rare.
  10541.  
  10542.  
  10543.  
  10544. Resource Errors
  10545.  
  10546.  
  10547. -11    Out of Memory
  10548.  
  10549.     This error occurs whenever there is not enough memory for CRDE
  10550.     to complete an operation.  Normally, this requirement is very
  10551.     low.  Because of the intelligent way in which it manages memory
  10552.     CRDE can perform even the most sophisticated operations in
  10553.     usually  < 32k.
  10554.  
  10555.     This error most commonly occurs when a large transaction has
  10556.     eaten up most available memory.  You should immediately
  10557.     rollback the transaction before continuing.
  10558.  
  10559.  
  10560. -16    Integrity Check
  10561.  
  10562.     CRDE tried to open a table which could not pass even the most
  10563.     basic of integrity checks.  It is very possible that the file
  10564.     that was opened was not even a CRDE table.  Otherwise, the table
  10565.     has a corrupt header, at the very least.  Use trepair to fix
  10566.     the table.
  10567.  
  10568.  
  10569.  
  10570. Operating System Errors
  10571.  
  10572.  
  10573. -21    Disk Seek
  10574.  
  10575.     A disk seek error occurred.
  10576.  
  10577.  
  10578.  
  10579.                     - 173 -
  10580.  
  10581. -22    Disk Read
  10582.  
  10583.     A disk read error occurred.
  10584.  
  10585.  
  10586. -23    Disk Write
  10587.  
  10588.     A disk write error occurred.
  10589.  
  10590.  
  10591. -24    File Create
  10592.  
  10593.     CRDE was unable to create a file.  This error will often
  10594.     occur when too many file handles are already open.
  10595.  
  10596.  
  10597. -25    File Open
  10598.  
  10599.     CRDE was unable to open a file.  This error can occur if
  10600.     too many file handles are open.
  10601.  
  10602.  
  10603. -26    File Close
  10604.  
  10605.     An error occurred while CRDE was trying to close a table.
  10606.  
  10607.  
  10608.  
  10609. Internal Errors
  10610.  
  10611.  
  10612. -31    Internal Error
  10613.  
  10614.     CRDE has detected an inconsistancy in its internal buffers.
  10615.     This is usually an indication of some kind of memory corruption.
  10616.     Your program may have accidentally written into the buffer
  10617.     area by way of an unallocated pointer.
  10618.  
  10619.  
  10620. -32      Internal Error
  10621.  
  10622.     Same as -31.
  10623.  
  10624.  
  10625.  
  10626. Key/Row Violations
  10627.  
  10628.  
  10629. -41    Key Exists
  10630.  
  10631.     CRDE tried to insert a row into a keyed table for which a key
  10632.     already exists.  -41 is unique in that it does not specify an
  10633.     error, rather an indication that the row was not inserted.
  10634.     Unlike other errors, CRDE will continue to process a function
  10635.     if an error -41 occurs.
  10636.  
  10637.  
  10638.  
  10639.                     - 174 -
  10640.  
  10641. -42    Key not found
  10642.  
  10643.     CRDE was unable to find a matching key for a row in the table.
  10644.     This is usually a sign of index corruption.
  10645.  
  10646.  
  10647. -51    Row previously deleted
  10648.  
  10649.     CRDE tried to delete aa row which was already marked as deleted.
  10650.     This is usually a sign of index corruption.
  10651.  
  10652.  
  10653.  
  10654. Parsing Errors
  10655.  
  10656.  
  10657. -101    Bad table name
  10658.  
  10659.     A table name must either be TEMP, STATIC, as defined in crde.h,
  10660.     or a legal DOS pathname <= 64 characters not containing wildcards
  10661.     or an extension.
  10662.  
  10663.  
  10664. -102    Token too long
  10665.  
  10666.     CRDE tried a parse a token which was too long for that particular
  10667.     context.
  10668.  
  10669.  
  10670. -103    Empty column list
  10671.  
  10672.     The column list, table descriptor, index descriptor, etc. must
  10673.     contain at least one column name.
  10674.  
  10675.  
  10676. -104    Column list too long
  10677.  
  10678.     There were too many columns listed in the column list for that
  10679.     particular context.
  10680.  
  10681.  
  10682. -105    Syntax error
  10683.  
  10684.     A general syntax error occurred.
  10685.  
  10686.  
  10687.  
  10688. Table Limit Errors
  10689.  
  10690.  
  10691. -111    Column too large
  10692.  
  10693.     A column may not be more than 256 characters in length.
  10694.  
  10695.  
  10696.  
  10697.  
  10698.  
  10699.                     - 175 -
  10700.  
  10701. -112    Key too large
  10702.  
  10703.     The combined total size of all columns in a key may not be
  10704.     larger than 256 bytes.
  10705.  
  10706.  
  10707. -113    Row too large
  10708.  
  10709.     The combined total size of all columns in a row may not be more
  10710.     than 4000 bytes.
  10711.  
  10712.  
  10713. -114    Too many columns
  10714.  
  10715.     A table may consist of a maximum  of 256 columns.  An index key
  10716.     may consist of a maximum of 8 columns.
  10717.  
  10718.  
  10719. -115    Too many indexes
  10720.  
  10721.     A table may not have more than 8 secondary indexes.
  10722.  
  10723.  
  10724. -116     No columns in this table
  10725.  
  10726.     A table must consist of at least one column.
  10727.  
  10728.  
  10729. -117    Too many tables
  10730.  
  10731.     CRDE imposes a restriction of no more than 128 open tables at
  10732.     any one time.
  10733.  
  10734.  
  10735.  
  10736. Syntax Errors
  10737.  
  10738.  
  10739. -121    Illegal datatype
  10740.  
  10741.     You specified an illegal datatype in a table descriptor.  Legal
  10742.     datatypes are c, i, l, d, f, and $.
  10743.  
  10744.  
  10745. -122    Unknown column
  10746.  
  10747.     You specified a column in a table which CRDE could not recognize.
  10748.     This error can often occur when you forget to end a search
  10749.     expression/change list with a terminating NULL.
  10750.  
  10751.  
  10752. -123    Unknown index
  10753.  
  10754.     You specified an index in a table which CRDE could not recognize.
  10755.  
  10756.  
  10757.  
  10758.  
  10759.                     - 176 -
  10760.  
  10761. -124    Duplicate column
  10762.  
  10763.     A column name appears more than once in a column list.
  10764.  
  10765.  
  10766. -125    Duplicte key column
  10767.  
  10768.     A key may not contain duplicate columns.
  10769.  
  10770.  
  10771.  
  10772. System Errors
  10773.  
  10774.  
  10775. -131    Invalid table
  10776.  
  10777.     A TABLE * does not point to an open table.
  10778.  
  10779.  
  10780. -132    Table is not modifiable
  10781.  
  10782.     You tried to modify a table which CRDE has placed in read-only
  10783.     mode.  CRDE does this whenever you call one of its "..if"
  10784.     functions.  The host table is placed in read-only mode until
  10785.     the "..if" functions terminates.
  10786.  
  10787.  
  10788. -133    Operation restricted by transaction
  10789.  
  10790.     Some CRDE functions are not permissible during a transaction.
  10791.     You may not create or drop secondary indexes during a
  10792.     transaction.
  10793.  
  10794.  
  10795. -134    Corrupt table
  10796.  
  10797.     CRDE has determined that a table is corrupt.  Use trepair to
  10798.     fix the table.
  10799.  
  10800.  
  10801. -135    Duplicate tables
  10802.  
  10803.     You tried to perform a two table operation on a single table.
  10804.     This is not allowed.
  10805.  
  10806.  
  10807. -136    Corrupt index
  10808.  
  10809.     CRDE has determined that one or more of a table's indexes are
  10810.     corrupt.  Rebuild the indexes.
  10811.  
  10812.  
  10813. -137    Table cannot be temporary
  10814.  
  10815.     Use of a temporary table, TEMP or STATIC, is not permissable
  10816.     in this context.
  10817.  
  10818.  
  10819.                     - 177 -
  10820.  
  10821. Search/Change Expression Errors
  10822.  
  10823.  
  10824. -141    Illegal relop
  10825.  
  10826.     You specified an illegal relop in a search expression.  Legal
  10827.     relops defined in crde. h are EQ, GT, LT, GE, LE, NE.
  10828.  
  10829.  
  10830. -142    Expression too complex
  10831.  
  10832.     A search expression may not contain more than 16 conditions.
  10833.  
  10834.  
  10835. -143    Too many changes
  10836.  
  10837.     A change list may not contain more than 16 changes.
  10838.  
  10839.  
  10840.  
  10841. Miscelleneous Errors
  10842.  
  10843.  
  10844. -151    Join columns do not match
  10845.  
  10846.     The join columns of two tables must be identical in both type
  10847.     and size on a column by column basis.
  10848.  
  10849.  
  10850. -152    Incompatible structures
  10851.  
  10852.     A two-table CRDE function requires that the tables have
  10853.     compatible structure, i.e. they must be identicial in both
  10854.     type and size on a column by column basis.  The tables may
  10855.     have different primary and secondary keys.
  10856.  
  10857.  
  10858. -153    Table is not tcrstruct compatible
  10859.  
  10860.     The first two columns of a table passed to the tcrstruct
  10861.     function must be c columns.  tcrstruct imposes no other
  10862.     restriction upon the table.
  10863.  
  10864.  
  10865. -154    Bad tview specs.
  10866.  
  10867.     You specified a bad window for the tview function.  A window
  10868.     must be within the coordinates 1,1 to 80,25 and must have a
  10869.     width of at least 5 and a height of at least 1.
  10870.  
  10871.  
  10872. -155    Unable to convert data
  10873.  
  10874.     An import or export operation was unable to convert data
  10875.     between    CRDE and the foreign data type.  This error may also
  10876.     occur when trying to change a date column to something other
  10877.     than a char column with trestruct.
  10878.  
  10879.                     - 178 -